git@vger.kernel.org list mirror (unofficial, one of many)
 help / color / mirror / Atom feed
* [PATCH] userdiff: add support for Emacs Lisp
@ 2021-02-13 19:24 Adam Spiers
  2021-02-14  1:41 ` Ævar Arnfjörð Bjarmason
  0 siblings, 1 reply; 154+ messages in thread
From: Adam Spiers @ 2021-02-13 19:24 UTC (permalink / raw)
  To: git list; +Cc: Protesilaos Stavrou

Add a diff driver which recognises Elisp top-level forms and outline
headings for display in hunk headers, and which correctly renders word
diffs.

This approach was previously discussed on the emacs-devel mailing list:

   https://lists.gnu.org/archive/html/emacs-devel/2021-02/msg00705.html

* userdiff.c (builtin_drivers): Provide regexen for Elisp top level
  forms and outline headings, and for word diffs.
* t/t4018-diff-funcname.sh (diffpatterns): Add test for elisp driver
* t/t4018/elisp-outline-heading: Test fixture for outline headings
* t/t4018/elisp-top-level-form: Test fixture for top level forms
* t/t4034-diff-words.sh: Add test for elisp driver
* t/t4034/elisp/*: Test fixtures for word diffs

Signed-off-by: Protesilaos Stavrou <info@protesilaos.com>
Signed-off-by: Adam Spiers <git@adamspiers.org>
---
 Documentation/gitattributes.txt | 2 ++
 t/t4018-diff-funcname.sh        | 1 +
 t/t4018/elisp-outline-heading   | 6 ++++++
 t/t4018/elisp-top-level-form    | 7 +++++++
 t/t4034-diff-words.sh           | 1 +
 t/t4034/elisp/expect            | 9 +++++++++
 t/t4034/elisp/post              | 4 ++++
 t/t4034/elisp/pre               | 4 ++++
 userdiff.c                      | 9 +++++++++
 9 files changed, 43 insertions(+)
 create mode 100644 t/t4018/elisp-outline-heading
 create mode 100644 t/t4018/elisp-top-level-form
 create mode 100644 t/t4034/elisp/expect
 create mode 100644 t/t4034/elisp/post
 create mode 100644 t/t4034/elisp/pre

diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index e84e104f93..0026055f99 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -815,6 +815,8 @@ patterns are available:
 
 - `dts` suitable for devicetree (DTS) files.
 
+- `elisp` suitable for source code in the Emacs Lisp language.
+
 - `elixir` suitable for source code in the Elixir language.
 
 - `fortran` suitable for source code in the Fortran language.
diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index 9675bc17db..a9ea2d3cd5 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -33,6 +33,7 @@ diffpatterns="
 	csharp
 	css
 	dts
+	elisp
 	elixir
 	fortran
 	fountain
diff --git a/t/t4018/elisp-outline-heading b/t/t4018/elisp-outline-heading
new file mode 100644
index 0000000000..c13bdafafe
--- /dev/null
+++ b/t/t4018/elisp-outline-heading
@@ -0,0 +1,6 @@
+;;; A top-level outline heading
+;;;; A second-level outline heading RIGHT
+
+;; This is a ChangeMe comment outside top-level forms
+(defun foo ()
+  (bar 1 2 3)
diff --git a/t/t4018/elisp-top-level-form b/t/t4018/elisp-top-level-form
new file mode 100644
index 0000000000..683f7ffcf1
--- /dev/null
+++ b/t/t4018/elisp-top-level-form
@@ -0,0 +1,7 @@
+;;; Outline heading
+
+;; This is a comment
+(RIGHT
+  (list 1 2 3)
+  ChangeMe
+  (list a b c))
diff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh
index 0c8fb39ced..a546ee831a 100755
--- a/t/t4034-diff-words.sh
+++ b/t/t4034-diff-words.sh
@@ -315,6 +315,7 @@ test_language_driver cpp
 test_language_driver csharp
 test_language_driver css
 test_language_driver dts
+test_language_driver elisp
 test_language_driver fortran
 test_language_driver html
 test_language_driver java
diff --git a/t/t4034/elisp/expect b/t/t4034/elisp/expect
new file mode 100644
index 0000000000..29a6ef2520
--- /dev/null
+++ b/t/t4034/elisp/expect
@@ -0,0 +1,9 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 4a39df8..6619e96 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,4 +1,4 @@<RESET>
+(defun <RED>myfunc<RESET><GREEN>my-func<RESET> (<RED>a b<RESET><GREEN>first second<RESET>)
+  "This is a <RED>really<RESET><GREEN>(moderately)<RESET> cool function."
+  (let ((c (<RED>+ a b<RESET><GREEN>1+ first<RESET>)))
+    (format "one more than the total is %d" (<RED>1+<RESET><GREEN>+<RESET> c <GREEN>second<RESET>))))
diff --git a/t/t4034/elisp/post b/t/t4034/elisp/post
new file mode 100644
index 0000000000..6619e96657
--- /dev/null
+++ b/t/t4034/elisp/post
@@ -0,0 +1,4 @@
+(defun my-func (first second)
+  "This is a (moderately) cool function."
+  (let ((c (1+ first)))
+    (format "one more than the total is %d" (+ c second))))
diff --git a/t/t4034/elisp/pre b/t/t4034/elisp/pre
new file mode 100644
index 0000000000..4a39df8ffb
--- /dev/null
+++ b/t/t4034/elisp/pre
@@ -0,0 +1,4 @@
+(defun myfunc (a b)
+  "This is a really cool function."
+  (let ((c (+ a b)))
+    (format "one more than the total is %d" (1+ c))))
diff --git a/userdiff.c b/userdiff.c
index 3f81a2261c..292e51674a 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -53,6 +53,15 @@ PATTERNS("dts",
 	 /* Property names and math operators */
 	 "[a-zA-Z0-9,._+?#-]+"
 	 "|[-+*/%&^|!~]|>>|<<|&&|\\|\\|"),
+PATTERNS("elisp",
+	 /* Top level forms and outline headings */
+	 "^((\\(|;;;+ +).+)",
+	 /*
+	  * Emacs Lisp allows symbol names containing any characters.
+	  * However spaces within the symbol must be escaped.
+	  */
+	 "(\\.|[^ ()])+"
+	 ),
 PATTERNS("elixir",
 	 "^[ \t]*((def(macro|module|impl|protocol|p)?|test)[ \t].*)$",
 	 /* -- */
-- 
2.30.1.490.ge54fde04c8.dirty


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

* Re: [PATCH] userdiff: add support for Emacs Lisp
  2021-02-13 19:24 [PATCH] userdiff: add support for Emacs Lisp Adam Spiers
@ 2021-02-14  1:41 ` Ævar Arnfjörð Bjarmason
  2021-02-14  8:12   ` Johannes Sixt
  0 siblings, 1 reply; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-14  1:41 UTC (permalink / raw)
  To: Adam Spiers; +Cc: git list, Protesilaos Stavrou


On Sat, Feb 13 2021, Adam Spiers wrote:

> Add a diff driver which recognises Elisp top-level forms and outline
> headings for display in hunk headers, and which correctly renders word
> diffs.

Neat, I for one would find this useful.

> This approach was previously discussed on the emacs-devel mailing list:
>
>    https://lists.gnu.org/archive/html/emacs-devel/2021-02/msg00705.html
>
> * userdiff.c (builtin_drivers): Provide regexen for Elisp top level
>   forms and outline headings, and for word diffs.
> * t/t4018-diff-funcname.sh (diffpatterns): Add test for elisp driver
> * t/t4018/elisp-outline-heading: Test fixture for outline headings
> * t/t4018/elisp-top-level-form: Test fixture for top level forms
> * t/t4034-diff-words.sh: Add test for elisp driver
> * t/t4034/elisp/*: Test fixtures for word diffs

Please no on the overly verbose emacs.git convention of per-file
changelogs in commit messages :)

> diff --git a/t/t4018/elisp-outline-heading b/t/t4018/elisp-outline-heading
> new file mode 100644
> index 0000000000..c13bdafafe
> --- /dev/null
> +++ b/t/t4018/elisp-outline-heading
> @@ -0,0 +1,6 @@
> +;;; A top-level outline heading
> +;;;; A second-level outline heading RIGHT
> +
> +;; This is a ChangeMe comment outside top-level forms
> +(defun foo ()
> +  (bar 1 2 3)
> diff --git a/t/t4018/elisp-top-level-form b/t/t4018/elisp-top-level-form
> new file mode 100644
> index 0000000000..683f7ffcf1
> --- /dev/null
> +++ b/t/t4018/elisp-top-level-form
> @@ -0,0 +1,7 @@
> +;;; Outline heading
> +
> +;; This is a comment
> +(RIGHT
> +  (list 1 2 3)
> +  ChangeMe
> +  (list a b c))
> diff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh
> index 0c8fb39ced..a546ee831a 100755
> --- a/t/t4034-diff-words.sh
> +++ b/t/t4034-diff-words.sh
> @@ -315,6 +315,7 @@ test_language_driver cpp
>  test_language_driver csharp
>  test_language_driver css
>  test_language_driver dts
> +test_language_driver elisp
>  test_language_driver fortran
>  test_language_driver html
>  test_language_driver java
> diff --git a/t/t4034/elisp/expect b/t/t4034/elisp/expect
> new file mode 100644
> index 0000000000..29a6ef2520
> --- /dev/null
> +++ b/t/t4034/elisp/expect
> @@ -0,0 +1,9 @@
> +<BOLD>diff --git a/pre b/post<RESET>
> +<BOLD>index 4a39df8..6619e96 100644<RESET>
> +<BOLD>--- a/pre<RESET>
> +<BOLD>+++ b/post<RESET>
> +<CYAN>@@ -1,4 +1,4 @@<RESET>
> +(defun <RED>myfunc<RESET><GREEN>my-func<RESET> (<RED>a b<RESET><GREEN>first second<RESET>)
> +  "This is a <RED>really<RESET><GREEN>(moderately)<RESET> cool function."
> +  (let ((c (<RED>+ a b<RESET><GREEN>1+ first<RESET>)))
> +    (format "one more than the total is %d" (<RED>1+<RESET><GREEN>+<RESET> c <GREEN>second<RESET>))))
> diff --git a/t/t4034/elisp/post b/t/t4034/elisp/post
> new file mode 100644
> index 0000000000..6619e96657
> --- /dev/null
> +++ b/t/t4034/elisp/post
> @@ -0,0 +1,4 @@
> +(defun my-func (first second)
> +  "This is a (moderately) cool function."
> +  (let ((c (1+ first)))
> +    (format "one more than the total is %d" (+ c second))))
> diff --git a/t/t4034/elisp/pre b/t/t4034/elisp/pre
> new file mode 100644
> index 0000000000..4a39df8ffb
> --- /dev/null
> +++ b/t/t4034/elisp/pre
> @@ -0,0 +1,4 @@
> +(defun myfunc (a b)
> +  "This is a really cool function."
> +  (let ((c (+ a b)))
> +    (format "one more than the total is %d" (1+ c))))
> diff --git a/userdiff.c b/userdiff.c
> index 3f81a2261c..292e51674a 100644
> --- a/userdiff.c
> +++ b/userdiff.c
> @@ -53,6 +53,15 @@ PATTERNS("dts",
>  	 /* Property names and math operators */
>  	 "[a-zA-Z0-9,._+?#-]+"
>  	 "|[-+*/%&^|!~]|>>|<<|&&|\\|\\|"),
> +PATTERNS("elisp",
> +	 /* Top level forms and outline headings */
> +	 "^((\\(|;;;+ +).+)",
> +	 /*
> +	  * Emacs Lisp allows symbol names containing any characters.
> +	  * However spaces within the symbol must be escaped.
> +	  */
> +	 "(\\.|[^ ()])+"
> +	 ),
>  PATTERNS("elixir",
>  	 "^[ \t]*((def(macro|module|impl|protocol|p)?|test)[ \t].*)$",
>  	 /* -- */

I think this patch would benefit from first being dogfooded in the
emacs-devel community. There's an existing regex in emacs.git's
autoconf.sh which anyone developing emacs.git is using.

If you run:

    diff -u <(git -c diff.elisp.xfuncname='^\(def[^[:space:]]+[[:space:]]+([^()[:space:]]+)' log -p -- lisp | grep -m 100 @@) \
	    <(git -c diff.elisp.xfuncname='^((\(|;;;+ +).*)$' log -p -- lisp | grep -m 100 @@) \
    | less

You can see how it differs from yours, and that also neatly answers the
question[1] you had in the linked thread about why these patterns tend
to be explicitly scoped to how you define a function/package/whatever in
the language in question.

Just a cursory "git log -p -- lisp" in emacs.git with your patch shows
e.g. lisp/thingatpt.el where forms in a "defun" aren't indented (before
it selects the "defun", after with yours it's a "put" in the middle of
the function).

Yours also changes it from e.g.:

    @@ -61,7 +61,7 @@ forward-thing

to:

    @@ -61,7 +61,7 @@ (defun forward-thing (thing &optional n)

Is this really desired in elisp? I also note how our tests in
t4018-diff-funcname.sh are really bad in not testing for this at
all. I.e. we just test that we match the right line, not how we extract
a match from it.

1. https://lists.gnu.org/archive/html/emacs-devel/2021-02/msg00739.html

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

* Re: [PATCH] userdiff: add support for Emacs Lisp
  2021-02-14  1:41 ` Ævar Arnfjörð Bjarmason
@ 2021-02-14  8:12   ` Johannes Sixt
  2021-02-14 11:10     ` Johannes Sixt
  2021-02-14 18:25     ` Ævar Arnfjörð Bjarmason
  0 siblings, 2 replies; 154+ messages in thread
From: Johannes Sixt @ 2021-02-14  8:12 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, Adam Spiers
  Cc: git list, Protesilaos Stavrou

Am 14.02.21 um 02:41 schrieb Ævar Arnfjörð Bjarmason:
> Just a cursory "git log -p -- lisp" in emacs.git with your patch shows
> e.g. lisp/thingatpt.el where forms in a "defun" aren't indented (before
> it selects the "defun", after with yours it's a "put" in the middle of
> the function).

Note that negative matches can be specified. We use the feature in the 
cpp case to exclude public:/protected:/private: and label: that happen 
to be not indented. Perhaps that can be useful here?

Oh, and BTW, what the patterns treat as "function" must not match what 
the language treats as function. The purpose of the hunk header is to 
spot a place in the source file easily. So, it does not hurt if 
eval-and-compile forms are captured (as was mentioned in the linked 
thread) if desired.

> 
> Yours also changes it from e.g.:
> 
>      @@ -61,7 +61,7 @@ forward-thing
> 
> to:
> 
>      @@ -61,7 +61,7 @@ (defun forward-thing (thing &optional n)
> 
> Is this really desired in elisp?

It's common practice to extract the entire line (sans indentation if 
applicable), not just the function name. Why would somebody want the 
latter? It doesn't carry as much information as could be possible.

> I also note how our tests in
> t4018-diff-funcname.sh are really bad in not testing for this at
> all. I.e. we just test that we match the right line, not how we extract
> a match from it.

Oh, well. These are "semi-automated" tests cases. If you have an idea 
how to achieve the goal without burdening test writers with lots of 
subtleties, be my guest. ;)

> 
> 1. https://lists.gnu.org/archive/html/emacs-devel/2021-02/msg00739.html
> 

-- Hannes

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

* Re: [PATCH] userdiff: add support for Emacs Lisp
  2021-02-14  8:12   ` Johannes Sixt
@ 2021-02-14 11:10     ` Johannes Sixt
  2021-02-14 18:25     ` Ævar Arnfjörð Bjarmason
  1 sibling, 0 replies; 154+ messages in thread
From: Johannes Sixt @ 2021-02-14 11:10 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, Adam Spiers
  Cc: git list, Protesilaos Stavrou

Am 14.02.21 um 09:12 schrieb Johannes Sixt:
> Am 14.02.21 um 02:41 schrieb Ævar Arnfjörð Bjarmason:
>> Just a cursory "git log -p -- lisp" in emacs.git with your patch shows
>> e.g. lisp/thingatpt.el where forms in a "defun" aren't indented (before
>> it selects the "defun", after with yours it's a "put" in the middle of
>> the function).
> 
> Note that negative matches can be specified. We use the feature in the 
> cpp case to exclude public:/protected:/private: and label: that happen 
> to be not indented. Perhaps that can be useful here?
> 
> Oh, and BTW, what the patterns treat as "function" must not match what 

I meant to say "need not match".

> the language treats as function. The purpose of the hunk header is to 
> spot a place in the source file easily. So, it does not hurt if 
> eval-and-compile forms are captured (as was mentioned in the linked 
> thread) if desired.
> 
>>
>> Yours also changes it from e.g.:
>>
>>      @@ -61,7 +61,7 @@ forward-thing
>>
>> to:
>>
>>      @@ -61,7 +61,7 @@ (defun forward-thing (thing &optional n)
>>
>> Is this really desired in elisp?
> 
> It's common practice to extract the entire line (sans indentation if 
> applicable), not just the function name. Why would somebody want the 
> latter? It doesn't carry as much information as could be possible.
> 
>> I also note how our tests in
>> t4018-diff-funcname.sh are really bad in not testing for this at
>> all. I.e. we just test that we match the right line, not how we extract
>> a match from it.
> 
> Oh, well. These are "semi-automated" tests cases. If you have an idea 
> how to achieve the goal without burdening test writers with lots of 
> subtleties, be my guest. ;)
> 
>>
>> 1. https://lists.gnu.org/archive/html/emacs-devel/2021-02/msg00739.html
>>
> 
> -- Hannes


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

* Re: [PATCH] userdiff: add support for Emacs Lisp
  2021-02-14  8:12   ` Johannes Sixt
  2021-02-14 11:10     ` Johannes Sixt
@ 2021-02-14 18:25     ` Ævar Arnfjörð Bjarmason
  2021-02-15  0:52       ` [PATCH 00/20] userdiff: refactor + test + doc + misc improvements Ævar Arnfjörð Bjarmason
                         ` (21 more replies)
  1 sibling, 22 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-14 18:25 UTC (permalink / raw)
  To: Johannes Sixt; +Cc: Adam Spiers, git list, Protesilaos Stavrou


On Sun, Feb 14 2021, Johannes Sixt wrote:

> Am 14.02.21 um 02:41 schrieb Ævar Arnfjörð Bjarmason:
>> Just a cursory "git log -p -- lisp" in emacs.git with your patch shows
>> e.g. lisp/thingatpt.el where forms in a "defun" aren't indented (before
>> it selects the "defun", after with yours it's a "put" in the middle of
>> the function).
>
> Note that negative matches can be specified. We use the feature in the
> cpp case to exclude public:/protected:/private: and label: that happen 
> to be not indented. Perhaps that can be useful here?
>
> Oh, and BTW, what the patterns treat as "function" must not match what
> the language treats as function. The purpose of the hunk header is to 
> spot a place in the source file easily. So, it does not hurt if
> eval-and-compile forms are captured (as was mentioned in the linked 
> thread) if desired.

Right, so having lots of test-case is helpful, e.g. for elisp maybe you
have a top-level defun, maybe not, maybe the top-level is a "(progn" so
you'd like a second-level more meaningful context, or maybe not...

Obviously these userdiff patterns aren't a general parser and will
always be hit-and-miss, it's just useful to at least eyeball them
against in-the-wild test data to check their sanity & add some tests.

My cursory glance at the emacs.git version v.s. what's being submitted
here is that this one does a worse job in *very common* cases.

>> Yours also changes it from e.g.:
>>      @@ -61,7 +61,7 @@ forward-thing
>> to:
>>      @@ -61,7 +61,7 @@ (defun forward-thing (thing &optional n)
>> Is this really desired in elisp?
>
> It's common practice to extract the entire line (sans indentation if
> applicable), not just the function name. Why would somebody want the 
> latter? It doesn't carry as much information as could be possible.

Because I'm familiar with the codebase I'm editing and I just need to
know that the diff I'm viewing is on the "main" function, not that it's
"main()", "int main(int argv, char **argv)", "int main(const int argv,
const char *argv[])", or to either have a " {" or not at the end
depending on the project's coding style.

I know our own userdiff builtin patterns don't do this, but it would be
very useful to retrofit this capability / maybe make it a configurable
feature, i.e. have them capture the meaningful part of the line, and you
could either print it all, or just that part.

>> I also note how our tests in
>> t4018-diff-funcname.sh are really bad in not testing for this at
>> all. I.e. we just test that we match the right line, not how we extract
>> a match from it.
>
> Oh, well. These are "semi-automated" tests cases. If you have an idea
> how to achieve the goal without burdening test writers with lots of 
> subtleties, be my guest. ;)

I'll do that soon.

>> 1. https://lists.gnu.org/archive/html/emacs-devel/2021-02/msg00739.html
>> 
>
> -- Hannes


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

* [PATCH 00/20] userdiff: refactor + test + doc + misc improvements
  2021-02-14 18:25     ` Ævar Arnfjörð Bjarmason
@ 2021-02-15  0:52       ` Ævar Arnfjörð Bjarmason
  2021-02-15 15:44         ` [PATCH v2 00/27] " Ævar Arnfjörð Bjarmason
                           ` (27 more replies)
  2021-02-15  0:52       ` [PATCH 01/20] userdiff: refactor away the parse_bool() function Ævar Arnfjörð Bjarmason
                         ` (20 subsequent siblings)
  21 siblings, 28 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15  0:52 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers,
	Ævar Arnfjörð Bjarmason

This started out as a version of 07/20 since I noticed that we could
easily break userdiff patterns in subtle ways and and not notice,
since nothing tested the full @@ line (just whether we had a "RIGHT"
token).

But it grew into fixing warts in the code, tests docs, and I even
managed to improve the golang userdiff driver while I was at it.

Hopefully this isn't too hard to review, the diffstat is scary, but
it's mostly moving things around. I went out of my way to make 08/20
as easy as possible to review with the color-move detection.

Ævar Arnfjörð Bjarmason (20):
  userdiff: refactor away the parse_bool() function
  userdiff: re-order builtin drivers in alphabetical order
  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
  userdiff tests: match full hunk headers
  userdiff tests: rewrite hunk header test infrastructure
  blame tests: don't rely on t/t4018/ directory
  userdiff tests: move custom patterns into one test file
  userdiff tests: remove hack for "RIGHT" token
  userdiff: match "package" in diff=golang
  userdiff tests + docs: document & test "diff.<driver>.x?funcname"
  gitattributes doc: reword discussion of built-in userdiff patterns
  gitattributes doc: document multi-line userdiff patterns
  userdiff tests: remove "funcname" from custom3 test
  userdiff tests: factor out test_diff_funcname() logic
  userdiff tests: test hunk headers on accumulated files
  userdiff tests: test hunk header selection with -U0
  userdiff tests: assert empty hunk header context on -U<large>

 Documentation/config/diff.txt              |  12 ++
 Documentation/gitattributes.txt            |  33 ++-
 Makefile                                   |   1 +
 t/annotate-tests.sh                        |  16 +-
 t/helper/test-tool.c                       |   1 +
 t/helper/test-tool.h                       |   1 +
 t/helper/test-userdiff.c                   |  32 +++
 t/t4018-diff-funcname.sh                   | 154 +++++++------
 t/t4018/README                             |  18 --
 t/t4018/bash-arithmetic-function           |   4 -
 t/t4018/bash-bashism-style-compact         |   6 -
 t/t4018/bash-bashism-style-function        |   4 -
 t/t4018/bash-bashism-style-whitespace      |   4 -
 t/t4018/bash-conditional-function          |   4 -
 t/t4018/bash-missing-parentheses           |   6 -
 t/t4018/bash-mixed-style-compact           |   4 -
 t/t4018/bash-mixed-style-function          |   4 -
 t/t4018/bash-nested-functions              |   6 -
 t/t4018/bash-other-characters              |   4 -
 t/t4018/bash-posix-style-compact           |   4 -
 t/t4018/bash-posix-style-function          |   4 -
 t/t4018/bash-posix-style-whitespace        |   4 -
 t/t4018/bash-subshell-function             |   4 -
 t/t4018/bash-trailing-comment              |   4 -
 t/t4018/bash.sh                            | 160 ++++++++++++++
 t/t4018/cpp-c++-function                   |   4 -
 t/t4018/cpp-class-constructor              |   4 -
 t/t4018/cpp-class-constructor-mem-init     |   5 -
 t/t4018/cpp-class-definition               |   4 -
 t/t4018/cpp-class-definition-derived       |   5 -
 t/t4018/cpp-class-destructor               |   4 -
 t/t4018/cpp-function-returning-global-type |   4 -
 t/t4018/cpp-function-returning-nested      |   5 -
 t/t4018/cpp-function-returning-pointer     |   4 -
 t/t4018/cpp-function-returning-reference   |   4 -
 t/t4018/cpp-gnu-style-function             |   5 -
 t/t4018/cpp-namespace-definition           |   4 -
 t/t4018/cpp-operator-definition            |   4 -
 t/t4018/cpp-skip-access-specifiers         |   8 -
 t/t4018/cpp-skip-comment-block             |   9 -
 t/t4018/cpp-skip-labels                    |   8 -
 t/t4018/cpp-struct-definition              |   9 -
 t/t4018/cpp-struct-single-line             |   7 -
 t/t4018/cpp-template-function-definition   |   4 -
 t/t4018/cpp-union-definition               |   4 -
 t/t4018/cpp-void-c-function                |   4 -
 t/t4018/cpp.sh                             | 240 +++++++++++++++++++++
 t/t4018/css-attribute-value-selector       |   4 -
 t/t4018/css-block-level-@-statements       |  10 -
 t/t4018/css-brace-in-col-1                 |   5 -
 t/t4018/css-class-selector                 |   4 -
 t/t4018/css-colon-eol                      |   4 -
 t/t4018/css-colon-selector                 |   5 -
 t/t4018/css-common                         |   4 -
 t/t4018/css-id-selector                    |   4 -
 t/t4018/css-long-selector-list             |   6 -
 t/t4018/css-prop-sans-indent               |   5 -
 t/t4018/css-root-selector                  |   4 -
 t/t4018/css-short-selector-list            |   4 -
 t/t4018/css-trailing-space                 |   5 -
 t/t4018/css.sh                             | 146 +++++++++++++
 t/t4018/custom.sh                          | 160 ++++++++++++++
 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                         |   9 -
 t/t4018/dts-node-unitless                  |   8 -
 t/t4018/dts-nodes                          |   8 -
 t/t4018/dts-nodes-boolean-prop             |   9 -
 t/t4018/dts-nodes-comment1                 |   8 -
 t/t4018/dts-nodes-comment2                 |   8 -
 t/t4018/dts-nodes-multiline-prop           |  13 --
 t/t4018/dts-reference                      |   9 -
 t/t4018/dts-root                           |   5 -
 t/t4018/dts-root-comment                   |   8 -
 t/t4018/dts.sh                             | 149 +++++++++++++
 t/t4018/elixir-do-not-pick-end             |   5 -
 t/t4018/elixir-ex-unit-test                |   6 -
 t/t4018/elixir-function                    |   5 -
 t/t4018/elixir-macro                       |   5 -
 t/t4018/elixir-module                      |   9 -
 t/t4018/elixir-module-func                 |   8 -
 t/t4018/elixir-nested-module               |   9 -
 t/t4018/elixir-private-function            |   5 -
 t/t4018/elixir-protocol                    |   6 -
 t/t4018/elixir-protocol-implementation     |   5 -
 t/t4018/elixir.sh                          | 127 +++++++++++
 t/t4018/fortran-block-data                 |   5 -
 t/t4018/fortran-comment                    |  13 --
 t/t4018/fortran-comment-keyword            |  14 --
 t/t4018/fortran-comment-legacy             |  13 --
 t/t4018/fortran-comment-legacy-star        |  13 --
 t/t4018/fortran-external-function          |   9 -
 t/t4018/fortran-external-subroutine        |   5 -
 t/t4018/fortran-module                     |   5 -
 t/t4018/fortran-module-procedure           |  13 --
 t/t4018/fortran-program                    |   5 -
 t/t4018/fortran.sh                         | 159 ++++++++++++++
 t/t4018/fountain-scene                     |   4 -
 t/t4018/fountain.sh                        |  14 ++
 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/golang.sh                          |  69 ++++++
 t/t4018/java-class-member-function         |   8 -
 t/t4018/java.sh                            |  18 ++
 t/t4018/markdown-heading-indented          |   6 -
 t/t4018/markdown-heading-non-headings      |  17 --
 t/t4018/markdown.sh                        |  39 ++++
 t/t4018/matlab-class-definition            |   5 -
 t/t4018/matlab-function                    |   4 -
 t/t4018/matlab-octave-section-1            |   3 -
 t/t4018/matlab-octave-section-2            |   3 -
 t/t4018/matlab-section                     |   3 -
 t/t4018/matlab.sh                          |  55 +++++
 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                            |  78 +++++++
 t/t4018/php-abstract-class                 |   4 -
 t/t4018/php-abstract-method                |   7 -
 t/t4018/php-class                          |   4 -
 t/t4018/php-final-class                    |   4 -
 t/t4018/php-final-method                   |   7 -
 t/t4018/php-function                       |   4 -
 t/t4018/php-interface                      |   4 -
 t/t4018/php-method                         |   7 -
 t/t4018/php-trait                          |   7 -
 t/t4018/php.sh                             | 106 +++++++++
 t/t4018/python-async-def                   |   4 -
 t/t4018/python-class                       |   4 -
 t/t4018/python-def                         |   4 -
 t/t4018/python-indented-async-def          |   7 -
 t/t4018/python-indented-class              |   5 -
 t/t4018/python-indented-def                |   7 -
 t/t4018/python.sh                          |  71 ++++++
 t/t4018/rust-fn                            |   5 -
 t/t4018/rust-impl                          |   5 -
 t/t4018/rust-macro-rules                   |   6 -
 t/t4018/rust-struct                        |   5 -
 t/t4018/rust-trait                         |   5 -
 t/t4018/rust.sh                            |  60 ++++++
 userdiff.c                                 | 157 ++++++++------
 userdiff.h                                 |  15 ++
 148 files changed, 1937 insertions(+), 905 deletions(-)
 create mode 100644 t/helper/test-userdiff.c
 delete mode 100644 t/t4018/README
 delete mode 100644 t/t4018/bash-arithmetic-function
 delete mode 100644 t/t4018/bash-bashism-style-compact
 delete mode 100644 t/t4018/bash-bashism-style-function
 delete mode 100644 t/t4018/bash-bashism-style-whitespace
 delete mode 100644 t/t4018/bash-conditional-function
 delete mode 100644 t/t4018/bash-missing-parentheses
 delete mode 100644 t/t4018/bash-mixed-style-compact
 delete mode 100644 t/t4018/bash-mixed-style-function
 delete mode 100644 t/t4018/bash-nested-functions
 delete mode 100644 t/t4018/bash-other-characters
 delete mode 100644 t/t4018/bash-posix-style-compact
 delete mode 100644 t/t4018/bash-posix-style-function
 delete mode 100644 t/t4018/bash-posix-style-whitespace
 delete mode 100644 t/t4018/bash-subshell-function
 delete mode 100644 t/t4018/bash-trailing-comment
 create mode 100755 t/t4018/bash.sh
 delete mode 100644 t/t4018/cpp-c++-function
 delete mode 100644 t/t4018/cpp-class-constructor
 delete mode 100644 t/t4018/cpp-class-constructor-mem-init
 delete mode 100644 t/t4018/cpp-class-definition
 delete mode 100644 t/t4018/cpp-class-definition-derived
 delete mode 100644 t/t4018/cpp-class-destructor
 delete mode 100644 t/t4018/cpp-function-returning-global-type
 delete mode 100644 t/t4018/cpp-function-returning-nested
 delete mode 100644 t/t4018/cpp-function-returning-pointer
 delete mode 100644 t/t4018/cpp-function-returning-reference
 delete mode 100644 t/t4018/cpp-gnu-style-function
 delete mode 100644 t/t4018/cpp-namespace-definition
 delete mode 100644 t/t4018/cpp-operator-definition
 delete mode 100644 t/t4018/cpp-skip-access-specifiers
 delete mode 100644 t/t4018/cpp-skip-comment-block
 delete mode 100644 t/t4018/cpp-skip-labels
 delete mode 100644 t/t4018/cpp-struct-definition
 delete mode 100644 t/t4018/cpp-struct-single-line
 delete mode 100644 t/t4018/cpp-template-function-definition
 delete mode 100644 t/t4018/cpp-union-definition
 delete mode 100644 t/t4018/cpp-void-c-function
 create mode 100755 t/t4018/cpp.sh
 delete mode 100644 t/t4018/css-attribute-value-selector
 delete mode 100644 t/t4018/css-block-level-@-statements
 delete mode 100644 t/t4018/css-brace-in-col-1
 delete mode 100644 t/t4018/css-class-selector
 delete mode 100644 t/t4018/css-colon-eol
 delete mode 100644 t/t4018/css-colon-selector
 delete mode 100644 t/t4018/css-common
 delete mode 100644 t/t4018/css-id-selector
 delete mode 100644 t/t4018/css-long-selector-list
 delete mode 100644 t/t4018/css-prop-sans-indent
 delete mode 100644 t/t4018/css-root-selector
 delete mode 100644 t/t4018/css-short-selector-list
 delete mode 100644 t/t4018/css-trailing-space
 create mode 100755 t/t4018/css.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
 delete mode 100644 t/t4018/dts-labels
 delete mode 100644 t/t4018/dts-node-unitless
 delete mode 100644 t/t4018/dts-nodes
 delete mode 100644 t/t4018/dts-nodes-boolean-prop
 delete mode 100644 t/t4018/dts-nodes-comment1
 delete mode 100644 t/t4018/dts-nodes-comment2
 delete mode 100644 t/t4018/dts-nodes-multiline-prop
 delete mode 100644 t/t4018/dts-reference
 delete mode 100644 t/t4018/dts-root
 delete mode 100644 t/t4018/dts-root-comment
 create mode 100755 t/t4018/dts.sh
 delete mode 100644 t/t4018/elixir-do-not-pick-end
 delete mode 100644 t/t4018/elixir-ex-unit-test
 delete mode 100644 t/t4018/elixir-function
 delete mode 100644 t/t4018/elixir-macro
 delete mode 100644 t/t4018/elixir-module
 delete mode 100644 t/t4018/elixir-module-func
 delete mode 100644 t/t4018/elixir-nested-module
 delete mode 100644 t/t4018/elixir-private-function
 delete mode 100644 t/t4018/elixir-protocol
 delete mode 100644 t/t4018/elixir-protocol-implementation
 create mode 100755 t/t4018/elixir.sh
 delete mode 100644 t/t4018/fortran-block-data
 delete mode 100644 t/t4018/fortran-comment
 delete mode 100644 t/t4018/fortran-comment-keyword
 delete mode 100644 t/t4018/fortran-comment-legacy
 delete mode 100644 t/t4018/fortran-comment-legacy-star
 delete mode 100644 t/t4018/fortran-external-function
 delete mode 100644 t/t4018/fortran-external-subroutine
 delete mode 100644 t/t4018/fortran-module
 delete mode 100644 t/t4018/fortran-module-procedure
 delete mode 100644 t/t4018/fortran-program
 create mode 100755 t/t4018/fortran.sh
 delete mode 100644 t/t4018/fountain-scene
 create mode 100755 t/t4018/fountain.sh
 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
 create mode 100755 t/t4018/golang.sh
 delete mode 100644 t/t4018/java-class-member-function
 create mode 100755 t/t4018/java.sh
 delete mode 100644 t/t4018/markdown-heading-indented
 delete mode 100644 t/t4018/markdown-heading-non-headings
 create mode 100755 t/t4018/markdown.sh
 delete mode 100644 t/t4018/matlab-class-definition
 delete mode 100644 t/t4018/matlab-function
 delete mode 100644 t/t4018/matlab-octave-section-1
 delete mode 100644 t/t4018/matlab-octave-section-2
 delete mode 100644 t/t4018/matlab-section
 create mode 100755 t/t4018/matlab.sh
 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
 delete mode 100644 t/t4018/php-abstract-class
 delete mode 100644 t/t4018/php-abstract-method
 delete mode 100644 t/t4018/php-class
 delete mode 100644 t/t4018/php-final-class
 delete mode 100644 t/t4018/php-final-method
 delete mode 100644 t/t4018/php-function
 delete mode 100644 t/t4018/php-interface
 delete mode 100644 t/t4018/php-method
 delete mode 100644 t/t4018/php-trait
 create mode 100755 t/t4018/php.sh
 delete mode 100644 t/t4018/python-async-def
 delete mode 100644 t/t4018/python-class
 delete mode 100644 t/t4018/python-def
 delete mode 100644 t/t4018/python-indented-async-def
 delete mode 100644 t/t4018/python-indented-class
 delete mode 100644 t/t4018/python-indented-def
 create mode 100755 t/t4018/python.sh
 delete mode 100644 t/t4018/rust-fn
 delete mode 100644 t/t4018/rust-impl
 delete mode 100644 t/t4018/rust-macro-rules
 delete mode 100644 t/t4018/rust-struct
 delete mode 100644 t/t4018/rust-trait
 create mode 100755 t/t4018/rust.sh

-- 
2.30.0.284.gd98b1dd5eaa7


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

* [PATCH 01/20] userdiff: refactor away the parse_bool() function
  2021-02-14 18:25     ` Ævar Arnfjörð Bjarmason
  2021-02-15  0:52       ` [PATCH 00/20] userdiff: refactor + test + doc + misc improvements Ævar Arnfjörð Bjarmason
@ 2021-02-15  0:52       ` Ævar Arnfjörð Bjarmason
  2021-02-15  0:52       ` [PATCH 02/20] userdiff: re-order builtin drivers in alphabetical order Ævar Arnfjörð Bjarmason
                         ` (19 subsequent siblings)
  21 siblings, 0 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15  0:52 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers,
	Ævar Arnfjörð Bjarmason

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 <avarab@gmail.com>
---
 userdiff.c | 18 ++++++------------
 1 file changed, 6 insertions(+), 12 deletions(-)

diff --git a/userdiff.c b/userdiff.c
index 3f81a2261c..c147bcbb17 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


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

* [PATCH 02/20] userdiff: re-order builtin drivers in alphabetical order
  2021-02-14 18:25     ` Ævar Arnfjörð Bjarmason
  2021-02-15  0:52       ` [PATCH 00/20] userdiff: refactor + test + doc + misc improvements Ævar Arnfjörð Bjarmason
  2021-02-15  0:52       ` [PATCH 01/20] userdiff: refactor away the parse_bool() function Ævar Arnfjörð Bjarmason
@ 2021-02-15  0:52       ` Ævar Arnfjörð Bjarmason
  2021-02-15  0:52       ` [PATCH 03/20] userdiff: add and use for_each_userdiff_driver() Ævar Arnfjörð Bjarmason
                         ` (18 subsequent siblings)
  21 siblings, 0 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15  0:52 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers,
	Ævar Arnfjörð Bjarmason

Address some old code smell and move around the built-in userdiff
definitions 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 <avarab@gmail.com>
---
 userdiff.c | 76 +++++++++++++++++++++++++++---------------------------
 1 file changed, 38 insertions(+), 38 deletions(-)

diff --git a/userdiff.c b/userdiff.c
index c147bcbb17..c92cbcc054 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


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

* [PATCH 03/20] userdiff: add and use for_each_userdiff_driver()
  2021-02-14 18:25     ` Ævar Arnfjörð Bjarmason
                         ` (2 preceding siblings ...)
  2021-02-15  0:52       ` [PATCH 02/20] userdiff: re-order builtin drivers in alphabetical order Ævar Arnfjörð Bjarmason
@ 2021-02-15  0:52       ` Ævar Arnfjörð Bjarmason
  2021-02-15  0:52       ` [PATCH 04/20] userdiff tests: explicitly test "default" pattern Ævar Arnfjörð Bjarmason
                         ` (17 subsequent siblings)
  21 siblings, 0 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15  0:52 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers,
	Ævar Arnfjörð Bjarmason

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 <avarab@gmail.com>
---
 userdiff.c | 61 +++++++++++++++++++++++++++++++++++++++++++-----------
 userdiff.h | 15 ++++++++++++++
 2 files changed, 64 insertions(+), 12 deletions(-)

diff --git a/userdiff.c b/userdiff.c
index c92cbcc054..92b5a97e12 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -250,20 +250,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,
@@ -364,3 +376,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 203057e13e..fe14014a77 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


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

* [PATCH 04/20] userdiff tests: explicitly test "default" pattern
  2021-02-14 18:25     ` Ævar Arnfjörð Bjarmason
                         ` (3 preceding siblings ...)
  2021-02-15  0:52       ` [PATCH 03/20] userdiff: add and use for_each_userdiff_driver() Ævar Arnfjörð Bjarmason
@ 2021-02-15  0:52       ` Ævar Arnfjörð Bjarmason
  2021-02-15  0:52       ` [PATCH 05/20] userdiff tests: list builtin drivers via test-tool Ævar Arnfjörð Bjarmason
                         ` (16 subsequent siblings)
  21 siblings, 0 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15  0:52 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers,
	Ævar Arnfjörð Bjarmason

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 <avarab@gmail.com>
---
 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 9675bc17db..cefe329aea 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


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

* [PATCH 05/20] userdiff tests: list builtin drivers via test-tool
  2021-02-14 18:25     ` Ævar Arnfjörð Bjarmason
                         ` (4 preceding siblings ...)
  2021-02-15  0:52       ` [PATCH 04/20] userdiff tests: explicitly test "default" pattern Ævar Arnfjörð Bjarmason
@ 2021-02-15  0:52       ` Ævar Arnfjörð Bjarmason
  2021-02-15  1:24         ` Eric Sunshine
  2021-02-15  0:52       ` [PATCH 06/20] userdiff: remove support for "broken" tests Ævar Arnfjörð Bjarmason
                         ` (15 subsequent siblings)
  21 siblings, 1 reply; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15  0:52 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers,
	Ævar Arnfjörð Bjarmason

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 <avarab@gmail.com>
---
 Makefile                 |  1 +
 t/helper/test-tool.c     |  1 +
 t/helper/test-tool.h     |  1 +
 t/helper/test-userdiff.c | 32 ++++++++++++++++++++++++++++++++
 t/t4018-diff-funcname.sh | 29 +++++------------------------
 5 files changed, 40 insertions(+), 24 deletions(-)
 create mode 100644 t/helper/test-userdiff.c

diff --git a/Makefile b/Makefile
index 5a239cac20..710a0deaed 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 f97cd9f48a..dcb05ca6e5 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 28072c0ad5..589f2e8ac6 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 0000000000..80de456287
--- /dev/null
+++ b/t/helper/test-userdiff.c
@@ -0,0 +1,32 @@
+#include "test-tool.h"
+#include "cache.h"
+#include "userdiff.h"
+
+static int userdiff_list_builtin_drivers_cb(struct userdiff_driver *driver,
+					    enum userdiff_driver_type type,
+					    void *priv)
+{
+	puts(driver->name);
+	return 0;
+}
+
+static int list_what(enum userdiff_driver_type type)
+{
+	return for_each_userdiff_driver(userdiff_list_builtin_drivers_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 cefe329aea..11ac648451 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -8,6 +8,10 @@ test_description='Test custom diff function name patterns'
 . ./test-lib.sh
 
 test_expect_success 'setup' '
+	test-tool userdiff list-builtin-drivers >builtin-drivers &&
+	test_file_not_empty builtin-drivers &&
+	builtin_drivers=$(cat builtin-drivers) &&
+
 	# a non-trivial custom pattern
 	git config diff.custom1.funcname "!static
 !String
@@ -26,30 +30,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
+	$builtin_drivers
 	custom1
 	custom2
 	custom3
-- 
2.30.0.284.gd98b1dd5eaa7


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

* [PATCH 06/20] userdiff: remove support for "broken" tests
  2021-02-14 18:25     ` Ævar Arnfjörð Bjarmason
                         ` (5 preceding siblings ...)
  2021-02-15  0:52       ` [PATCH 05/20] userdiff tests: list builtin drivers via test-tool Ævar Arnfjörð Bjarmason
@ 2021-02-15  0:52       ` Ævar Arnfjörð Bjarmason
  2021-02-15  0:52       ` [PATCH 07/20] userdiff tests: match full hunk headers Ævar Arnfjörð Bjarmason
                         ` (14 subsequent siblings)
  21 siblings, 0 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15  0:52 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers,
	Ævar Arnfjörð Bjarmason

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 <avarab@gmail.com>
---
 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 11ac648451..5fb5b0a651 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -83,13 +83,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 283e01cca1..2d25b2b4fc 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


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

* [PATCH 07/20] userdiff tests: match full hunk headers
  2021-02-14 18:25     ` Ævar Arnfjörð Bjarmason
                         ` (6 preceding siblings ...)
  2021-02-15  0:52       ` [PATCH 06/20] userdiff: remove support for "broken" tests Ævar Arnfjörð Bjarmason
@ 2021-02-15  0:52       ` Ævar Arnfjörð Bjarmason
  2021-02-15  1:35         ` Eric Sunshine
  2021-02-15  0:52       ` [PATCH 08/20] userdiff tests: rewrite hunk header test infrastructure Ævar Arnfjörð Bjarmason
                         ` (13 subsequent siblings)
  21 siblings, 1 reply; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15  0:52 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers,
	Ævar Arnfjörð Bjarmason

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 adding corresponding *.ctx
files, this has the added advantage that we're doing a "test_cmp", so
when we have failures it's just a non-zero exit code from "grep",
we'll actually have something meaningful in the "-v" output.

As we'll see in a follow-up commit this is an intermediate step
towards even better test coverage. I'm structuring these tests in such
a way as to benefit from the diff.colorMove detection.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 t/t4018-diff-funcname.sh                      |  7 +++---
 t/t4018/README                                | 22 +++++++++----------
 t/t4018/README.ctx                            |  1 +
 t/t4018/bash-arithmetic-function.ctx          |  1 +
 t/t4018/bash-bashism-style-compact.ctx        |  1 +
 t/t4018/bash-bashism-style-function.ctx       |  1 +
 t/t4018/bash-bashism-style-whitespace.ctx     |  1 +
 t/t4018/bash-conditional-function.ctx         |  1 +
 t/t4018/bash-missing-parentheses.ctx          |  1 +
 t/t4018/bash-mixed-style-compact.ctx          |  1 +
 t/t4018/bash-mixed-style-function.ctx         |  1 +
 t/t4018/bash-nested-functions.ctx             |  1 +
 t/t4018/bash-other-characters.ctx             |  1 +
 t/t4018/bash-posix-style-compact.ctx          |  1 +
 t/t4018/bash-posix-style-function.ctx         |  1 +
 t/t4018/bash-posix-style-whitespace.ctx       |  1 +
 t/t4018/bash-subshell-function.ctx            |  1 +
 t/t4018/bash-trailing-comment.ctx             |  1 +
 t/t4018/cpp-c++-function.ctx                  |  1 +
 t/t4018/cpp-class-constructor-mem-init.ctx    |  1 +
 t/t4018/cpp-class-constructor.ctx             |  1 +
 t/t4018/cpp-class-definition-derived.ctx      |  1 +
 t/t4018/cpp-class-definition.ctx              |  1 +
 t/t4018/cpp-class-destructor.ctx              |  1 +
 .../cpp-function-returning-global-type.ctx    |  1 +
 t/t4018/cpp-function-returning-nested.ctx     |  1 +
 t/t4018/cpp-function-returning-pointer.ctx    |  1 +
 t/t4018/cpp-function-returning-reference.ctx  |  1 +
 t/t4018/cpp-gnu-style-function.ctx            |  1 +
 t/t4018/cpp-namespace-definition.ctx          |  1 +
 t/t4018/cpp-operator-definition.ctx           |  1 +
 t/t4018/cpp-skip-access-specifiers.ctx        |  1 +
 t/t4018/cpp-skip-comment-block.ctx            |  1 +
 t/t4018/cpp-skip-labels.ctx                   |  1 +
 t/t4018/cpp-struct-definition.ctx             |  1 +
 t/t4018/cpp-struct-single-line.ctx            |  1 +
 t/t4018/cpp-template-function-definition.ctx  |  1 +
 t/t4018/cpp-union-definition.ctx              |  1 +
 t/t4018/cpp-void-c-function.ctx               |  1 +
 t/t4018/css-attribute-value-selector.ctx      |  1 +
 t/t4018/css-block-level-@-statements.ctx      |  1 +
 t/t4018/css-brace-in-col-1.ctx                |  1 +
 t/t4018/css-class-selector.ctx                |  1 +
 t/t4018/css-colon-eol.ctx                     |  1 +
 t/t4018/css-colon-selector.ctx                |  1 +
 t/t4018/css-common.ctx                        |  1 +
 t/t4018/css-id-selector.ctx                   |  1 +
 t/t4018/css-long-selector-list.ctx            |  1 +
 t/t4018/css-prop-sans-indent.ctx              |  1 +
 t/t4018/css-root-selector.ctx                 |  1 +
 t/t4018/css-short-selector-list.ctx           |  1 +
 t/t4018/css-trailing-space.ctx                |  1 +
 t/t4018/custom1-pattern.ctx                   |  1 +
 t/t4018/custom2-match-to-end-of-line.ctx      |  1 +
 t/t4018/custom3-alternation-in-pattern.ctx    |  1 +
 t/t4018/dts-labels.ctx                        |  1 +
 t/t4018/dts-node-unitless.ctx                 |  1 +
 t/t4018/dts-nodes-boolean-prop.ctx            |  1 +
 t/t4018/dts-nodes-comment1.ctx                |  1 +
 t/t4018/dts-nodes-comment2.ctx                |  1 +
 t/t4018/dts-nodes-multiline-prop.ctx          |  1 +
 t/t4018/dts-nodes.ctx                         |  1 +
 t/t4018/dts-reference.ctx                     |  1 +
 t/t4018/dts-root-comment.ctx                  |  1 +
 t/t4018/dts-root.ctx                          |  1 +
 t/t4018/elixir-do-not-pick-end.ctx            |  1 +
 t/t4018/elixir-ex-unit-test.ctx               |  1 +
 t/t4018/elixir-function.ctx                   |  1 +
 t/t4018/elixir-macro.ctx                      |  1 +
 t/t4018/elixir-module-func.ctx                |  1 +
 t/t4018/elixir-module.ctx                     |  1 +
 t/t4018/elixir-nested-module.ctx              |  1 +
 t/t4018/elixir-private-function.ctx           |  1 +
 t/t4018/elixir-protocol-implementation.ctx    |  1 +
 t/t4018/elixir-protocol.ctx                   |  1 +
 t/t4018/fortran-block-data.ctx                |  1 +
 t/t4018/fortran-comment-keyword.ctx           |  1 +
 t/t4018/fortran-comment-legacy-star.ctx       |  1 +
 t/t4018/fortran-comment-legacy.ctx            |  1 +
 t/t4018/fortran-comment.ctx                   |  1 +
 t/t4018/fortran-external-function.ctx         |  1 +
 t/t4018/fortran-external-subroutine.ctx       |  1 +
 t/t4018/fortran-module-procedure.ctx          |  1 +
 t/t4018/fortran-module.ctx                    |  1 +
 t/t4018/fortran-program.ctx                   |  1 +
 t/t4018/fountain-scene.ctx                    |  1 +
 t/t4018/golang-complex-function.ctx           |  1 +
 t/t4018/golang-func.ctx                       |  1 +
 t/t4018/golang-interface.ctx                  |  1 +
 t/t4018/golang-long-func.ctx                  |  1 +
 t/t4018/golang-struct.ctx                     |  1 +
 t/t4018/java-class-member-function.ctx        |  1 +
 t/t4018/markdown-heading-indented.ctx         |  1 +
 t/t4018/markdown-heading-non-headings.ctx     |  1 +
 t/t4018/matlab-class-definition.ctx           |  1 +
 t/t4018/matlab-function.ctx                   |  1 +
 t/t4018/matlab-octave-section-1.ctx           |  1 +
 t/t4018/matlab-octave-section-2.ctx           |  1 +
 t/t4018/matlab-section.ctx                    |  1 +
 t/t4018/perl-skip-end-of-heredoc.ctx          |  1 +
 t/t4018/perl-skip-forward-decl.ctx            |  1 +
 t/t4018/perl-skip-sub-in-pod.ctx              |  1 +
 t/t4018/perl-sub-definition-kr-brace.ctx      |  1 +
 t/t4018/perl-sub-definition.ctx               |  1 +
 t/t4018/php-abstract-class.ctx                |  1 +
 t/t4018/php-abstract-method.ctx               |  1 +
 t/t4018/php-class.ctx                         |  1 +
 t/t4018/php-final-class.ctx                   |  1 +
 t/t4018/php-final-method.ctx                  |  1 +
 t/t4018/php-function.ctx                      |  1 +
 t/t4018/php-interface.ctx                     |  1 +
 t/t4018/php-method.ctx                        |  1 +
 t/t4018/php-trait.ctx                         |  1 +
 t/t4018/python-async-def.ctx                  |  1 +
 t/t4018/python-class.ctx                      |  1 +
 t/t4018/python-def.ctx                        |  1 +
 t/t4018/python-indented-async-def.ctx         |  1 +
 t/t4018/python-indented-class.ctx             |  1 +
 t/t4018/python-indented-def.ctx               |  1 +
 t/t4018/rust-fn.ctx                           |  1 +
 t/t4018/rust-impl.ctx                         |  1 +
 t/t4018/rust-macro-rules.ctx                  |  1 +
 t/t4018/rust-struct.ctx                       |  1 +
 t/t4018/rust-trait.ctx                        |  1 +
 124 files changed, 137 insertions(+), 14 deletions(-)
 create mode 100644 t/t4018/README.ctx
 create mode 100644 t/t4018/bash-arithmetic-function.ctx
 create mode 100644 t/t4018/bash-bashism-style-compact.ctx
 create mode 100644 t/t4018/bash-bashism-style-function.ctx
 create mode 100644 t/t4018/bash-bashism-style-whitespace.ctx
 create mode 100644 t/t4018/bash-conditional-function.ctx
 create mode 100644 t/t4018/bash-missing-parentheses.ctx
 create mode 100644 t/t4018/bash-mixed-style-compact.ctx
 create mode 100644 t/t4018/bash-mixed-style-function.ctx
 create mode 100644 t/t4018/bash-nested-functions.ctx
 create mode 100644 t/t4018/bash-other-characters.ctx
 create mode 100644 t/t4018/bash-posix-style-compact.ctx
 create mode 100644 t/t4018/bash-posix-style-function.ctx
 create mode 100644 t/t4018/bash-posix-style-whitespace.ctx
 create mode 100644 t/t4018/bash-subshell-function.ctx
 create mode 100644 t/t4018/bash-trailing-comment.ctx
 create mode 100644 t/t4018/cpp-c++-function.ctx
 create mode 100644 t/t4018/cpp-class-constructor-mem-init.ctx
 create mode 100644 t/t4018/cpp-class-constructor.ctx
 create mode 100644 t/t4018/cpp-class-definition-derived.ctx
 create mode 100644 t/t4018/cpp-class-definition.ctx
 create mode 100644 t/t4018/cpp-class-destructor.ctx
 create mode 100644 t/t4018/cpp-function-returning-global-type.ctx
 create mode 100644 t/t4018/cpp-function-returning-nested.ctx
 create mode 100644 t/t4018/cpp-function-returning-pointer.ctx
 create mode 100644 t/t4018/cpp-function-returning-reference.ctx
 create mode 100644 t/t4018/cpp-gnu-style-function.ctx
 create mode 100644 t/t4018/cpp-namespace-definition.ctx
 create mode 100644 t/t4018/cpp-operator-definition.ctx
 create mode 100644 t/t4018/cpp-skip-access-specifiers.ctx
 create mode 100644 t/t4018/cpp-skip-comment-block.ctx
 create mode 100644 t/t4018/cpp-skip-labels.ctx
 create mode 100644 t/t4018/cpp-struct-definition.ctx
 create mode 100644 t/t4018/cpp-struct-single-line.ctx
 create mode 100644 t/t4018/cpp-template-function-definition.ctx
 create mode 100644 t/t4018/cpp-union-definition.ctx
 create mode 100644 t/t4018/cpp-void-c-function.ctx
 create mode 100644 t/t4018/css-attribute-value-selector.ctx
 create mode 100644 t/t4018/css-block-level-@-statements.ctx
 create mode 100644 t/t4018/css-brace-in-col-1.ctx
 create mode 100644 t/t4018/css-class-selector.ctx
 create mode 100644 t/t4018/css-colon-eol.ctx
 create mode 100644 t/t4018/css-colon-selector.ctx
 create mode 100644 t/t4018/css-common.ctx
 create mode 100644 t/t4018/css-id-selector.ctx
 create mode 100644 t/t4018/css-long-selector-list.ctx
 create mode 100644 t/t4018/css-prop-sans-indent.ctx
 create mode 100644 t/t4018/css-root-selector.ctx
 create mode 100644 t/t4018/css-short-selector-list.ctx
 create mode 100644 t/t4018/css-trailing-space.ctx
 create mode 100644 t/t4018/custom1-pattern.ctx
 create mode 100644 t/t4018/custom2-match-to-end-of-line.ctx
 create mode 100644 t/t4018/custom3-alternation-in-pattern.ctx
 create mode 100644 t/t4018/dts-labels.ctx
 create mode 100644 t/t4018/dts-node-unitless.ctx
 create mode 100644 t/t4018/dts-nodes-boolean-prop.ctx
 create mode 100644 t/t4018/dts-nodes-comment1.ctx
 create mode 100644 t/t4018/dts-nodes-comment2.ctx
 create mode 100644 t/t4018/dts-nodes-multiline-prop.ctx
 create mode 100644 t/t4018/dts-nodes.ctx
 create mode 100644 t/t4018/dts-reference.ctx
 create mode 100644 t/t4018/dts-root-comment.ctx
 create mode 100644 t/t4018/dts-root.ctx
 create mode 100644 t/t4018/elixir-do-not-pick-end.ctx
 create mode 100644 t/t4018/elixir-ex-unit-test.ctx
 create mode 100644 t/t4018/elixir-function.ctx
 create mode 100644 t/t4018/elixir-macro.ctx
 create mode 100644 t/t4018/elixir-module-func.ctx
 create mode 100644 t/t4018/elixir-module.ctx
 create mode 100644 t/t4018/elixir-nested-module.ctx
 create mode 100644 t/t4018/elixir-private-function.ctx
 create mode 100644 t/t4018/elixir-protocol-implementation.ctx
 create mode 100644 t/t4018/elixir-protocol.ctx
 create mode 100644 t/t4018/fortran-block-data.ctx
 create mode 100644 t/t4018/fortran-comment-keyword.ctx
 create mode 100644 t/t4018/fortran-comment-legacy-star.ctx
 create mode 100644 t/t4018/fortran-comment-legacy.ctx
 create mode 100644 t/t4018/fortran-comment.ctx
 create mode 100644 t/t4018/fortran-external-function.ctx
 create mode 100644 t/t4018/fortran-external-subroutine.ctx
 create mode 100644 t/t4018/fortran-module-procedure.ctx
 create mode 100644 t/t4018/fortran-module.ctx
 create mode 100644 t/t4018/fortran-program.ctx
 create mode 100644 t/t4018/fountain-scene.ctx
 create mode 100644 t/t4018/golang-complex-function.ctx
 create mode 100644 t/t4018/golang-func.ctx
 create mode 100644 t/t4018/golang-interface.ctx
 create mode 100644 t/t4018/golang-long-func.ctx
 create mode 100644 t/t4018/golang-struct.ctx
 create mode 100644 t/t4018/java-class-member-function.ctx
 create mode 100644 t/t4018/markdown-heading-indented.ctx
 create mode 100644 t/t4018/markdown-heading-non-headings.ctx
 create mode 100644 t/t4018/matlab-class-definition.ctx
 create mode 100644 t/t4018/matlab-function.ctx
 create mode 100644 t/t4018/matlab-octave-section-1.ctx
 create mode 100644 t/t4018/matlab-octave-section-2.ctx
 create mode 100644 t/t4018/matlab-section.ctx
 create mode 100644 t/t4018/perl-skip-end-of-heredoc.ctx
 create mode 100644 t/t4018/perl-skip-forward-decl.ctx
 create mode 100644 t/t4018/perl-skip-sub-in-pod.ctx
 create mode 100644 t/t4018/perl-sub-definition-kr-brace.ctx
 create mode 100644 t/t4018/perl-sub-definition.ctx
 create mode 100644 t/t4018/php-abstract-class.ctx
 create mode 100644 t/t4018/php-abstract-method.ctx
 create mode 100644 t/t4018/php-class.ctx
 create mode 100644 t/t4018/php-final-class.ctx
 create mode 100644 t/t4018/php-final-method.ctx
 create mode 100644 t/t4018/php-function.ctx
 create mode 100644 t/t4018/php-interface.ctx
 create mode 100644 t/t4018/php-method.ctx
 create mode 100644 t/t4018/php-trait.ctx
 create mode 100644 t/t4018/python-async-def.ctx
 create mode 100644 t/t4018/python-class.ctx
 create mode 100644 t/t4018/python-def.ctx
 create mode 100644 t/t4018/python-indented-async-def.ctx
 create mode 100644 t/t4018/python-indented-class.ctx
 create mode 100644 t/t4018/python-indented-def.ctx
 create mode 100644 t/t4018/rust-fn.ctx
 create mode 100644 t/t4018/rust-impl.ctx
 create mode 100644 t/t4018/rust-macro-rules.ctx
 create mode 100644 t/t4018/rust-struct.ctx
 create mode 100644 t/t4018/rust-trait.ctx

diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index 5fb5b0a651..d32c38ad1a 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -81,11 +81,12 @@ test_expect_success 'setup hunk header tests' '
 '
 
 # check each individual file
-for i in $(git ls-files)
+for i in $(git ls-files -- ':!*.ctx')
 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' <diff >ctx &&
+		test_cmp $i.ctx ctx
 	"
 done
 
diff --git a/t/t4018/README b/t/t4018/README
index 2d25b2b4fc..2cccf8c950 100644
--- a/t/t4018/README
+++ b/t/t4018/README
@@ -1,15 +1,15 @@
-How to write RIGHT test 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.
 
 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.
-
-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.
-
-BTW, this tests that the head line goes to the hunk header, not the line
-of equal signs.
+The text that must appear in the hunk header must contains the word
+"RIGHT" by convention. The "LANG-whatever.ctx" file contains what we
+expect to appear in the hunk header. We munged the start of the line
+to "@@ [...] @@" for ease of not having to hardcode the line numbers
+and offsets.
diff --git a/t/t4018/README.ctx b/t/t4018/README.ctx
new file mode 100644
index 0000000000..cd79384b04
--- /dev/null
+++ b/t/t4018/README.ctx
@@ -0,0 +1 @@
+description of the test.
diff --git a/t/t4018/bash-arithmetic-function.ctx b/t/t4018/bash-arithmetic-function.ctx
new file mode 100644
index 0000000000..811eac7d2f
--- /dev/null
+++ b/t/t4018/bash-arithmetic-function.ctx
@@ -0,0 +1 @@
+RIGHT()
diff --git a/t/t4018/bash-bashism-style-compact.ctx b/t/t4018/bash-bashism-style-compact.ctx
new file mode 100644
index 0000000000..4f8eac48c6
--- /dev/null
+++ b/t/t4018/bash-bashism-style-compact.ctx
@@ -0,0 +1 @@
+function RIGHT {
diff --git a/t/t4018/bash-bashism-style-function.ctx b/t/t4018/bash-bashism-style-function.ctx
new file mode 100644
index 0000000000..4f8eac48c6
--- /dev/null
+++ b/t/t4018/bash-bashism-style-function.ctx
@@ -0,0 +1 @@
+function RIGHT {
diff --git a/t/t4018/bash-bashism-style-whitespace.ctx b/t/t4018/bash-bashism-style-whitespace.ctx
new file mode 100644
index 0000000000..35dbd0220e
--- /dev/null
+++ b/t/t4018/bash-bashism-style-whitespace.ctx
@@ -0,0 +1 @@
+function 	RIGHT 	( 	) 	{
diff --git a/t/t4018/bash-conditional-function.ctx b/t/t4018/bash-conditional-function.ctx
new file mode 100644
index 0000000000..811eac7d2f
--- /dev/null
+++ b/t/t4018/bash-conditional-function.ctx
@@ -0,0 +1 @@
+RIGHT()
diff --git a/t/t4018/bash-missing-parentheses.ctx b/t/t4018/bash-missing-parentheses.ctx
new file mode 100644
index 0000000000..4f8eac48c6
--- /dev/null
+++ b/t/t4018/bash-missing-parentheses.ctx
@@ -0,0 +1 @@
+function RIGHT {
diff --git a/t/t4018/bash-mixed-style-compact.ctx b/t/t4018/bash-mixed-style-compact.ctx
new file mode 100644
index 0000000000..bba11074eb
--- /dev/null
+++ b/t/t4018/bash-mixed-style-compact.ctx
@@ -0,0 +1 @@
+function RIGHT(){
diff --git a/t/t4018/bash-mixed-style-function.ctx b/t/t4018/bash-mixed-style-function.ctx
new file mode 100644
index 0000000000..922b87a4aa
--- /dev/null
+++ b/t/t4018/bash-mixed-style-function.ctx
@@ -0,0 +1 @@
+function RIGHT() {
diff --git a/t/t4018/bash-nested-functions.ctx b/t/t4018/bash-nested-functions.ctx
new file mode 100644
index 0000000000..811eac7d2f
--- /dev/null
+++ b/t/t4018/bash-nested-functions.ctx
@@ -0,0 +1 @@
+RIGHT()
diff --git a/t/t4018/bash-other-characters.ctx b/t/t4018/bash-other-characters.ctx
new file mode 100644
index 0000000000..6a55317fdf
--- /dev/null
+++ b/t/t4018/bash-other-characters.ctx
@@ -0,0 +1 @@
+_RIGHT_0n()
diff --git a/t/t4018/bash-posix-style-compact.ctx b/t/t4018/bash-posix-style-compact.ctx
new file mode 100644
index 0000000000..811eac7d2f
--- /dev/null
+++ b/t/t4018/bash-posix-style-compact.ctx
@@ -0,0 +1 @@
+RIGHT()
diff --git a/t/t4018/bash-posix-style-function.ctx b/t/t4018/bash-posix-style-function.ctx
new file mode 100644
index 0000000000..811eac7d2f
--- /dev/null
+++ b/t/t4018/bash-posix-style-function.ctx
@@ -0,0 +1 @@
+RIGHT()
diff --git a/t/t4018/bash-posix-style-whitespace.ctx b/t/t4018/bash-posix-style-whitespace.ctx
new file mode 100644
index 0000000000..28f8698e14
--- /dev/null
+++ b/t/t4018/bash-posix-style-whitespace.ctx
@@ -0,0 +1 @@
+RIGHT 	( 	)
diff --git a/t/t4018/bash-subshell-function.ctx b/t/t4018/bash-subshell-function.ctx
new file mode 100644
index 0000000000..811eac7d2f
--- /dev/null
+++ b/t/t4018/bash-subshell-function.ctx
@@ -0,0 +1 @@
+RIGHT()
diff --git a/t/t4018/bash-trailing-comment.ctx b/t/t4018/bash-trailing-comment.ctx
new file mode 100644
index 0000000000..811eac7d2f
--- /dev/null
+++ b/t/t4018/bash-trailing-comment.ctx
@@ -0,0 +1 @@
+RIGHT()
diff --git a/t/t4018/cpp-c++-function.ctx b/t/t4018/cpp-c++-function.ctx
new file mode 100644
index 0000000000..337b49dbb3
--- /dev/null
+++ b/t/t4018/cpp-c++-function.ctx
@@ -0,0 +1 @@
+Item RIGHT::DoSomething( Args with_spaces )
diff --git a/t/t4018/cpp-class-constructor-mem-init.ctx b/t/t4018/cpp-class-constructor-mem-init.ctx
new file mode 100644
index 0000000000..6664b8b3d8
--- /dev/null
+++ b/t/t4018/cpp-class-constructor-mem-init.ctx
@@ -0,0 +1 @@
+Item::Item(int RIGHT) :
diff --git a/t/t4018/cpp-class-constructor.ctx b/t/t4018/cpp-class-constructor.ctx
new file mode 100644
index 0000000000..2dcadfc0ba
--- /dev/null
+++ b/t/t4018/cpp-class-constructor.ctx
@@ -0,0 +1 @@
+Item::Item(int RIGHT)
diff --git a/t/t4018/cpp-class-definition-derived.ctx b/t/t4018/cpp-class-definition-derived.ctx
new file mode 100644
index 0000000000..146f0a7b7c
--- /dev/null
+++ b/t/t4018/cpp-class-definition-derived.ctx
@@ -0,0 +1 @@
+class RIGHT :
diff --git a/t/t4018/cpp-class-definition.ctx b/t/t4018/cpp-class-definition.ctx
new file mode 100644
index 0000000000..54bff816d6
--- /dev/null
+++ b/t/t4018/cpp-class-definition.ctx
@@ -0,0 +1 @@
+class RIGHT
diff --git a/t/t4018/cpp-class-destructor.ctx b/t/t4018/cpp-class-destructor.ctx
new file mode 100644
index 0000000000..5390c17cf6
--- /dev/null
+++ b/t/t4018/cpp-class-destructor.ctx
@@ -0,0 +1 @@
+RIGHT::~RIGHT()
diff --git a/t/t4018/cpp-function-returning-global-type.ctx b/t/t4018/cpp-function-returning-global-type.ctx
new file mode 100644
index 0000000000..4dcdde25f4
--- /dev/null
+++ b/t/t4018/cpp-function-returning-global-type.ctx
@@ -0,0 +1 @@
+::Item get::it::RIGHT()
diff --git a/t/t4018/cpp-function-returning-nested.ctx b/t/t4018/cpp-function-returning-nested.ctx
new file mode 100644
index 0000000000..6ef73c8368
--- /dev/null
+++ b/t/t4018/cpp-function-returning-nested.ctx
@@ -0,0 +1 @@
+get::Item get::it::RIGHT()
diff --git a/t/t4018/cpp-function-returning-pointer.ctx b/t/t4018/cpp-function-returning-pointer.ctx
new file mode 100644
index 0000000000..bb0acce5c7
--- /dev/null
+++ b/t/t4018/cpp-function-returning-pointer.ctx
@@ -0,0 +1 @@
+const char *get_it_RIGHT(char *ptr)
diff --git a/t/t4018/cpp-function-returning-reference.ctx b/t/t4018/cpp-function-returning-reference.ctx
new file mode 100644
index 0000000000..76afe381fd
--- /dev/null
+++ b/t/t4018/cpp-function-returning-reference.ctx
@@ -0,0 +1 @@
+string& get::it::RIGHT(char *ptr)
diff --git a/t/t4018/cpp-gnu-style-function.ctx b/t/t4018/cpp-gnu-style-function.ctx
new file mode 100644
index 0000000000..1858287812
--- /dev/null
+++ b/t/t4018/cpp-gnu-style-function.ctx
@@ -0,0 +1 @@
+RIGHT(int arg)
diff --git a/t/t4018/cpp-namespace-definition.ctx b/t/t4018/cpp-namespace-definition.ctx
new file mode 100644
index 0000000000..14c29c4638
--- /dev/null
+++ b/t/t4018/cpp-namespace-definition.ctx
@@ -0,0 +1 @@
+namespace RIGHT
diff --git a/t/t4018/cpp-operator-definition.ctx b/t/t4018/cpp-operator-definition.ctx
new file mode 100644
index 0000000000..5b56778961
--- /dev/null
+++ b/t/t4018/cpp-operator-definition.ctx
@@ -0,0 +1 @@
+Value operator+(Value LEFT, Value RIGHT)
diff --git a/t/t4018/cpp-skip-access-specifiers.ctx b/t/t4018/cpp-skip-access-specifiers.ctx
new file mode 100644
index 0000000000..075bcd883b
--- /dev/null
+++ b/t/t4018/cpp-skip-access-specifiers.ctx
@@ -0,0 +1 @@
+class RIGHT : public Baseclass
diff --git a/t/t4018/cpp-skip-comment-block.ctx b/t/t4018/cpp-skip-comment-block.ctx
new file mode 100644
index 0000000000..656c59893d
--- /dev/null
+++ b/t/t4018/cpp-skip-comment-block.ctx
@@ -0,0 +1 @@
+struct item RIGHT(int i)
diff --git a/t/t4018/cpp-skip-labels.ctx b/t/t4018/cpp-skip-labels.ctx
new file mode 100644
index 0000000000..6b0635f7f7
--- /dev/null
+++ b/t/t4018/cpp-skip-labels.ctx
@@ -0,0 +1 @@
+void RIGHT (void)
diff --git a/t/t4018/cpp-struct-definition.ctx b/t/t4018/cpp-struct-definition.ctx
new file mode 100644
index 0000000000..48ed893279
--- /dev/null
+++ b/t/t4018/cpp-struct-definition.ctx
@@ -0,0 +1 @@
+struct RIGHT {
diff --git a/t/t4018/cpp-struct-single-line.ctx b/t/t4018/cpp-struct-single-line.ctx
new file mode 100644
index 0000000000..e3bc9d5017
--- /dev/null
+++ b/t/t4018/cpp-struct-single-line.ctx
@@ -0,0 +1 @@
+struct RIGHT_iterator_tag {};
diff --git a/t/t4018/cpp-template-function-definition.ctx b/t/t4018/cpp-template-function-definition.ctx
new file mode 100644
index 0000000000..c9da39cf65
--- /dev/null
+++ b/t/t4018/cpp-template-function-definition.ctx
@@ -0,0 +1 @@
+template<class T> int RIGHT(T arg)
diff --git a/t/t4018/cpp-union-definition.ctx b/t/t4018/cpp-union-definition.ctx
new file mode 100644
index 0000000000..2fc7b54fb8
--- /dev/null
+++ b/t/t4018/cpp-union-definition.ctx
@@ -0,0 +1 @@
+union RIGHT {
diff --git a/t/t4018/cpp-void-c-function.ctx b/t/t4018/cpp-void-c-function.ctx
new file mode 100644
index 0000000000..6b0635f7f7
--- /dev/null
+++ b/t/t4018/cpp-void-c-function.ctx
@@ -0,0 +1 @@
+void RIGHT (void)
diff --git a/t/t4018/css-attribute-value-selector.ctx b/t/t4018/css-attribute-value-selector.ctx
new file mode 100644
index 0000000000..7f8956251c
--- /dev/null
+++ b/t/t4018/css-attribute-value-selector.ctx
@@ -0,0 +1 @@
+[class*="RIGHT"] {
diff --git a/t/t4018/css-block-level-@-statements.ctx b/t/t4018/css-block-level-@-statements.ctx
new file mode 100644
index 0000000000..7f5e90468c
--- /dev/null
+++ b/t/t4018/css-block-level-@-statements.ctx
@@ -0,0 +1 @@
+@keyframes RIGHT {
diff --git a/t/t4018/css-brace-in-col-1.ctx b/t/t4018/css-brace-in-col-1.ctx
new file mode 100644
index 0000000000..91a9105c6a
--- /dev/null
+++ b/t/t4018/css-brace-in-col-1.ctx
@@ -0,0 +1 @@
+RIGHT label.control-label
diff --git a/t/t4018/css-class-selector.ctx b/t/t4018/css-class-selector.ctx
new file mode 100644
index 0000000000..ac7367d7f4
--- /dev/null
+++ b/t/t4018/css-class-selector.ctx
@@ -0,0 +1 @@
+.RIGHT {
diff --git a/t/t4018/css-colon-eol.ctx b/t/t4018/css-colon-eol.ctx
new file mode 100644
index 0000000000..b68493b9b0
--- /dev/null
+++ b/t/t4018/css-colon-eol.ctx
@@ -0,0 +1 @@
+RIGHT h1 {
diff --git a/t/t4018/css-colon-selector.ctx b/t/t4018/css-colon-selector.ctx
new file mode 100644
index 0000000000..00b1a5aefe
--- /dev/null
+++ b/t/t4018/css-colon-selector.ctx
@@ -0,0 +1 @@
+RIGHT a:hover {
diff --git a/t/t4018/css-common.ctx b/t/t4018/css-common.ctx
new file mode 100644
index 0000000000..43686b4081
--- /dev/null
+++ b/t/t4018/css-common.ctx
@@ -0,0 +1 @@
+RIGHT label.control-label {
diff --git a/t/t4018/css-id-selector.ctx b/t/t4018/css-id-selector.ctx
new file mode 100644
index 0000000000..ce19f6d8dc
--- /dev/null
+++ b/t/t4018/css-id-selector.ctx
@@ -0,0 +1 @@
+#RIGHT {
diff --git a/t/t4018/css-long-selector-list.ctx b/t/t4018/css-long-selector-list.ctx
new file mode 100644
index 0000000000..bc8d0fb62c
--- /dev/null
+++ b/t/t4018/css-long-selector-list.ctx
@@ -0,0 +1 @@
+div ul#RIGHT {
diff --git a/t/t4018/css-prop-sans-indent.ctx b/t/t4018/css-prop-sans-indent.ctx
new file mode 100644
index 0000000000..cc880b2f44
--- /dev/null
+++ b/t/t4018/css-prop-sans-indent.ctx
@@ -0,0 +1 @@
+RIGHT, label.control-label {
diff --git a/t/t4018/css-root-selector.ctx b/t/t4018/css-root-selector.ctx
new file mode 100644
index 0000000000..3010cded2a
--- /dev/null
+++ b/t/t4018/css-root-selector.ctx
@@ -0,0 +1 @@
+:RIGHT {
diff --git a/t/t4018/css-short-selector-list.ctx b/t/t4018/css-short-selector-list.ctx
new file mode 100644
index 0000000000..9e5d87d126
--- /dev/null
+++ b/t/t4018/css-short-selector-list.ctx
@@ -0,0 +1 @@
+label.control, div ul#RIGHT {
diff --git a/t/t4018/css-trailing-space.ctx b/t/t4018/css-trailing-space.ctx
new file mode 100644
index 0000000000..43686b4081
--- /dev/null
+++ b/t/t4018/css-trailing-space.ctx
@@ -0,0 +1 @@
+RIGHT label.control-label {
diff --git a/t/t4018/custom1-pattern.ctx b/t/t4018/custom1-pattern.ctx
new file mode 100644
index 0000000000..d1609cc9a6
--- /dev/null
+++ b/t/t4018/custom1-pattern.ctx
@@ -0,0 +1 @@
+int special, RIGHT;
diff --git a/t/t4018/custom2-match-to-end-of-line.ctx b/t/t4018/custom2-match-to-end-of-line.ctx
new file mode 100644
index 0000000000..8294c6e49b
--- /dev/null
+++ b/t/t4018/custom2-match-to-end-of-line.ctx
@@ -0,0 +1 @@
+RIGHT_Beer
diff --git a/t/t4018/custom3-alternation-in-pattern.ctx b/t/t4018/custom3-alternation-in-pattern.ctx
new file mode 100644
index 0000000000..2125474b68
--- /dev/null
+++ b/t/t4018/custom3-alternation-in-pattern.ctx
@@ -0,0 +1 @@
+public static void main(String RIGHT[])
diff --git a/t/t4018/dts-labels.ctx b/t/t4018/dts-labels.ctx
new file mode 100644
index 0000000000..48d9373cab
--- /dev/null
+++ b/t/t4018/dts-labels.ctx
@@ -0,0 +1 @@
+label2: RIGHT {
diff --git a/t/t4018/dts-node-unitless.ctx b/t/t4018/dts-node-unitless.ctx
new file mode 100644
index 0000000000..82c8683fa1
--- /dev/null
+++ b/t/t4018/dts-node-unitless.ctx
@@ -0,0 +1 @@
+RIGHT {
diff --git a/t/t4018/dts-nodes-boolean-prop.ctx b/t/t4018/dts-nodes-boolean-prop.ctx
new file mode 100644
index 0000000000..3a0232d55d
--- /dev/null
+++ b/t/t4018/dts-nodes-boolean-prop.ctx
@@ -0,0 +1 @@
+RIGHT@deadf00,4000 {
diff --git a/t/t4018/dts-nodes-comment1.ctx b/t/t4018/dts-nodes-comment1.ctx
new file mode 100644
index 0000000000..ec364600b1
--- /dev/null
+++ b/t/t4018/dts-nodes-comment1.ctx
@@ -0,0 +1 @@
+RIGHT@deadf00,4000 /* &a comment */ {
diff --git a/t/t4018/dts-nodes-comment2.ctx b/t/t4018/dts-nodes-comment2.ctx
new file mode 100644
index 0000000000..75f0d75258
--- /dev/null
+++ b/t/t4018/dts-nodes-comment2.ctx
@@ -0,0 +1 @@
+RIGHT@deadf00,4000 { /* a trailing comment */
diff --git a/t/t4018/dts-nodes-multiline-prop.ctx b/t/t4018/dts-nodes-multiline-prop.ctx
new file mode 100644
index 0000000000..3a0232d55d
--- /dev/null
+++ b/t/t4018/dts-nodes-multiline-prop.ctx
@@ -0,0 +1 @@
+RIGHT@deadf00,4000 {
diff --git a/t/t4018/dts-nodes.ctx b/t/t4018/dts-nodes.ctx
new file mode 100644
index 0000000000..3a0232d55d
--- /dev/null
+++ b/t/t4018/dts-nodes.ctx
@@ -0,0 +1 @@
+RIGHT@deadf00,4000 {
diff --git a/t/t4018/dts-reference.ctx b/t/t4018/dts-reference.ctx
new file mode 100644
index 0000000000..c1e13409ee
--- /dev/null
+++ b/t/t4018/dts-reference.ctx
@@ -0,0 +1 @@
+&RIGHT {
diff --git a/t/t4018/dts-root-comment.ctx b/t/t4018/dts-root-comment.ctx
new file mode 100644
index 0000000000..656053dd42
--- /dev/null
+++ b/t/t4018/dts-root-comment.ctx
@@ -0,0 +1 @@
+/ { RIGHT /* Technically just supposed to be a slash and brace */
diff --git a/t/t4018/dts-root.ctx b/t/t4018/dts-root.ctx
new file mode 100644
index 0000000000..656053dd42
--- /dev/null
+++ b/t/t4018/dts-root.ctx
@@ -0,0 +1 @@
+/ { RIGHT /* Technically just supposed to be a slash and brace */
diff --git a/t/t4018/elixir-do-not-pick-end.ctx b/t/t4018/elixir-do-not-pick-end.ctx
new file mode 100644
index 0000000000..8f28a7a689
--- /dev/null
+++ b/t/t4018/elixir-do-not-pick-end.ctx
@@ -0,0 +1 @@
+defmodule RIGHT do
diff --git a/t/t4018/elixir-ex-unit-test.ctx b/t/t4018/elixir-ex-unit-test.ctx
new file mode 100644
index 0000000000..a55e3de2cc
--- /dev/null
+++ b/t/t4018/elixir-ex-unit-test.ctx
@@ -0,0 +1 @@
+test "RIGHT" do
diff --git a/t/t4018/elixir-function.ctx b/t/t4018/elixir-function.ctx
new file mode 100644
index 0000000000..62aee9c8b1
--- /dev/null
+++ b/t/t4018/elixir-function.ctx
@@ -0,0 +1 @@
+def function(RIGHT, arg) do
diff --git a/t/t4018/elixir-macro.ctx b/t/t4018/elixir-macro.ctx
new file mode 100644
index 0000000000..fc1d3b85e8
--- /dev/null
+++ b/t/t4018/elixir-macro.ctx
@@ -0,0 +1 @@
+defmacro foo(RIGHT) do
diff --git a/t/t4018/elixir-module-func.ctx b/t/t4018/elixir-module-func.ctx
new file mode 100644
index 0000000000..8239214386
--- /dev/null
+++ b/t/t4018/elixir-module-func.ctx
@@ -0,0 +1 @@
+def fun(RIGHT) do
diff --git a/t/t4018/elixir-module.ctx b/t/t4018/elixir-module.ctx
new file mode 100644
index 0000000000..8f28a7a689
--- /dev/null
+++ b/t/t4018/elixir-module.ctx
@@ -0,0 +1 @@
+defmodule RIGHT do
diff --git a/t/t4018/elixir-nested-module.ctx b/t/t4018/elixir-nested-module.ctx
new file mode 100644
index 0000000000..3ffbdd18b1
--- /dev/null
+++ b/t/t4018/elixir-nested-module.ctx
@@ -0,0 +1 @@
+defmodule MyApp.RIGHT do
diff --git a/t/t4018/elixir-private-function.ctx b/t/t4018/elixir-private-function.ctx
new file mode 100644
index 0000000000..1c4eba44f7
--- /dev/null
+++ b/t/t4018/elixir-private-function.ctx
@@ -0,0 +1 @@
+defp function(RIGHT, arg) do
diff --git a/t/t4018/elixir-protocol-implementation.ctx b/t/t4018/elixir-protocol-implementation.ctx
new file mode 100644
index 0000000000..efb758aea6
--- /dev/null
+++ b/t/t4018/elixir-protocol-implementation.ctx
@@ -0,0 +1 @@
+defimpl RIGHT do
diff --git a/t/t4018/elixir-protocol.ctx b/t/t4018/elixir-protocol.ctx
new file mode 100644
index 0000000000..d0204e9f14
--- /dev/null
+++ b/t/t4018/elixir-protocol.ctx
@@ -0,0 +1 @@
+defprotocol RIGHT do
diff --git a/t/t4018/fortran-block-data.ctx b/t/t4018/fortran-block-data.ctx
new file mode 100644
index 0000000000..c3db084ccc
--- /dev/null
+++ b/t/t4018/fortran-block-data.ctx
@@ -0,0 +1 @@
+BLOCK DATA RIGHT
diff --git a/t/t4018/fortran-comment-keyword.ctx b/t/t4018/fortran-comment-keyword.ctx
new file mode 100644
index 0000000000..0b9220b355
--- /dev/null
+++ b/t/t4018/fortran-comment-keyword.ctx
@@ -0,0 +1 @@
+subroutine RIGHT (funcA, funcB)
diff --git a/t/t4018/fortran-comment-legacy-star.ctx b/t/t4018/fortran-comment-legacy-star.ctx
new file mode 100644
index 0000000000..6a34203f80
--- /dev/null
+++ b/t/t4018/fortran-comment-legacy-star.ctx
@@ -0,0 +1 @@
+subroutine RIGHT
diff --git a/t/t4018/fortran-comment-legacy.ctx b/t/t4018/fortran-comment-legacy.ctx
new file mode 100644
index 0000000000..6a34203f80
--- /dev/null
+++ b/t/t4018/fortran-comment-legacy.ctx
@@ -0,0 +1 @@
+subroutine RIGHT
diff --git a/t/t4018/fortran-comment.ctx b/t/t4018/fortran-comment.ctx
new file mode 100644
index 0000000000..6a34203f80
--- /dev/null
+++ b/t/t4018/fortran-comment.ctx
@@ -0,0 +1 @@
+subroutine RIGHT
diff --git a/t/t4018/fortran-external-function.ctx b/t/t4018/fortran-external-function.ctx
new file mode 100644
index 0000000000..56ec4d8eca
--- /dev/null
+++ b/t/t4018/fortran-external-function.ctx
@@ -0,0 +1 @@
+function RIGHT(a, b) result(c)
diff --git a/t/t4018/fortran-external-subroutine.ctx b/t/t4018/fortran-external-subroutine.ctx
new file mode 100644
index 0000000000..6a34203f80
--- /dev/null
+++ b/t/t4018/fortran-external-subroutine.ctx
@@ -0,0 +1 @@
+subroutine RIGHT
diff --git a/t/t4018/fortran-module-procedure.ctx b/t/t4018/fortran-module-procedure.ctx
new file mode 100644
index 0000000000..4f5ff2e4b8
--- /dev/null
+++ b/t/t4018/fortran-module-procedure.ctx
@@ -0,0 +1 @@
+module RIGHT
diff --git a/t/t4018/fortran-module.ctx b/t/t4018/fortran-module.ctx
new file mode 100644
index 0000000000..4f5ff2e4b8
--- /dev/null
+++ b/t/t4018/fortran-module.ctx
@@ -0,0 +1 @@
+module RIGHT
diff --git a/t/t4018/fortran-program.ctx b/t/t4018/fortran-program.ctx
new file mode 100644
index 0000000000..c4e844df30
--- /dev/null
+++ b/t/t4018/fortran-program.ctx
@@ -0,0 +1 @@
+program RIGHT
diff --git a/t/t4018/fountain-scene.ctx b/t/t4018/fountain-scene.ctx
new file mode 100644
index 0000000000..bf10171418
--- /dev/null
+++ b/t/t4018/fountain-scene.ctx
@@ -0,0 +1 @@
+EXT. STREET RIGHT OUTSIDE - DAY
diff --git a/t/t4018/golang-complex-function.ctx b/t/t4018/golang-complex-function.ctx
new file mode 100644
index 0000000000..8e8d5582ff
--- /dev/null
+++ b/t/t4018/golang-complex-function.ctx
@@ -0,0 +1 @@
+func (t *Test) RIGHT(a Type) (Type, error) {
diff --git a/t/t4018/golang-func.ctx b/t/t4018/golang-func.ctx
new file mode 100644
index 0000000000..88bc823813
--- /dev/null
+++ b/t/t4018/golang-func.ctx
@@ -0,0 +1 @@
+func RIGHT() {
diff --git a/t/t4018/golang-interface.ctx b/t/t4018/golang-interface.ctx
new file mode 100644
index 0000000000..2d07f5a383
--- /dev/null
+++ b/t/t4018/golang-interface.ctx
@@ -0,0 +1 @@
+type RIGHT interface {
diff --git a/t/t4018/golang-long-func.ctx b/t/t4018/golang-long-func.ctx
new file mode 100644
index 0000000000..25635e712e
--- /dev/null
+++ b/t/t4018/golang-long-func.ctx
@@ -0,0 +1 @@
+func RIGHT(aVeryVeryVeryLongVariableName AVeryVeryVeryLongType,
diff --git a/t/t4018/golang-struct.ctx b/t/t4018/golang-struct.ctx
new file mode 100644
index 0000000000..8a1240699d
--- /dev/null
+++ b/t/t4018/golang-struct.ctx
@@ -0,0 +1 @@
+type RIGHT struct {
diff --git a/t/t4018/java-class-member-function.ctx b/t/t4018/java-class-member-function.ctx
new file mode 100644
index 0000000000..2125474b68
--- /dev/null
+++ b/t/t4018/java-class-member-function.ctx
@@ -0,0 +1 @@
+public static void main(String RIGHT[])
diff --git a/t/t4018/markdown-heading-indented.ctx b/t/t4018/markdown-heading-indented.ctx
new file mode 100644
index 0000000000..5938336743
--- /dev/null
+++ b/t/t4018/markdown-heading-indented.ctx
@@ -0,0 +1 @@
+   ### RIGHT
diff --git a/t/t4018/markdown-heading-non-headings.ctx b/t/t4018/markdown-heading-non-headings.ctx
new file mode 100644
index 0000000000..7e2165be6e
--- /dev/null
+++ b/t/t4018/markdown-heading-non-headings.ctx
@@ -0,0 +1 @@
+# RIGHT
diff --git a/t/t4018/matlab-class-definition.ctx b/t/t4018/matlab-class-definition.ctx
new file mode 100644
index 0000000000..5dd5b45628
--- /dev/null
+++ b/t/t4018/matlab-class-definition.ctx
@@ -0,0 +1 @@
+classdef RIGHT
diff --git a/t/t4018/matlab-function.ctx b/t/t4018/matlab-function.ctx
new file mode 100644
index 0000000000..72d2350b13
--- /dev/null
+++ b/t/t4018/matlab-function.ctx
@@ -0,0 +1 @@
+function y = RIGHT()
diff --git a/t/t4018/matlab-octave-section-1.ctx b/t/t4018/matlab-octave-section-1.ctx
new file mode 100644
index 0000000000..ca9b349f94
--- /dev/null
+++ b/t/t4018/matlab-octave-section-1.ctx
@@ -0,0 +1 @@
+%%% RIGHT section
diff --git a/t/t4018/matlab-octave-section-2.ctx b/t/t4018/matlab-octave-section-2.ctx
new file mode 100644
index 0000000000..5cbb77faf5
--- /dev/null
+++ b/t/t4018/matlab-octave-section-2.ctx
@@ -0,0 +1 @@
+## RIGHT section
diff --git a/t/t4018/matlab-section.ctx b/t/t4018/matlab-section.ctx
new file mode 100644
index 0000000000..e83fee6f4d
--- /dev/null
+++ b/t/t4018/matlab-section.ctx
@@ -0,0 +1 @@
+%% RIGHT section
diff --git a/t/t4018/perl-skip-end-of-heredoc.ctx b/t/t4018/perl-skip-end-of-heredoc.ctx
new file mode 100644
index 0000000000..c15f4b78bd
--- /dev/null
+++ b/t/t4018/perl-skip-end-of-heredoc.ctx
@@ -0,0 +1 @@
+sub RIGHTwithheredocument {
diff --git a/t/t4018/perl-skip-forward-decl.ctx b/t/t4018/perl-skip-forward-decl.ctx
new file mode 100644
index 0000000000..e0c51599ad
--- /dev/null
+++ b/t/t4018/perl-skip-forward-decl.ctx
@@ -0,0 +1 @@
+package RIGHT;
diff --git a/t/t4018/perl-skip-sub-in-pod.ctx b/t/t4018/perl-skip-sub-in-pod.ctx
new file mode 100644
index 0000000000..abddd76655
--- /dev/null
+++ b/t/t4018/perl-skip-sub-in-pod.ctx
@@ -0,0 +1 @@
+=head1 SYNOPSIS_RIGHT
diff --git a/t/t4018/perl-sub-definition-kr-brace.ctx b/t/t4018/perl-sub-definition-kr-brace.ctx
new file mode 100644
index 0000000000..7e5aee5cde
--- /dev/null
+++ b/t/t4018/perl-sub-definition-kr-brace.ctx
@@ -0,0 +1 @@
+sub RIGHT
diff --git a/t/t4018/perl-sub-definition.ctx b/t/t4018/perl-sub-definition.ctx
new file mode 100644
index 0000000000..d49a63598e
--- /dev/null
+++ b/t/t4018/perl-sub-definition.ctx
@@ -0,0 +1 @@
+sub RIGHT {
diff --git a/t/t4018/php-abstract-class.ctx b/t/t4018/php-abstract-class.ctx
new file mode 100644
index 0000000000..f572d2129b
--- /dev/null
+++ b/t/t4018/php-abstract-class.ctx
@@ -0,0 +1 @@
+abstract class RIGHT
diff --git a/t/t4018/php-abstract-method.ctx b/t/t4018/php-abstract-method.ctx
new file mode 100644
index 0000000000..14cb6df42e
--- /dev/null
+++ b/t/t4018/php-abstract-method.ctx
@@ -0,0 +1 @@
+abstract public function RIGHT(): ?string
diff --git a/t/t4018/php-class.ctx b/t/t4018/php-class.ctx
new file mode 100644
index 0000000000..54bff816d6
--- /dev/null
+++ b/t/t4018/php-class.ctx
@@ -0,0 +1 @@
+class RIGHT
diff --git a/t/t4018/php-final-class.ctx b/t/t4018/php-final-class.ctx
new file mode 100644
index 0000000000..4d59fb749b
--- /dev/null
+++ b/t/t4018/php-final-class.ctx
@@ -0,0 +1 @@
+final class RIGHT
diff --git a/t/t4018/php-final-method.ctx b/t/t4018/php-final-method.ctx
new file mode 100644
index 0000000000..b7da8f8082
--- /dev/null
+++ b/t/t4018/php-final-method.ctx
@@ -0,0 +1 @@
+final public function RIGHT(): string
diff --git a/t/t4018/php-function.ctx b/t/t4018/php-function.ctx
new file mode 100644
index 0000000000..c5f3e55302
--- /dev/null
+++ b/t/t4018/php-function.ctx
@@ -0,0 +1 @@
+function RIGHT()
diff --git a/t/t4018/php-interface.ctx b/t/t4018/php-interface.ctx
new file mode 100644
index 0000000000..a45fa0532a
--- /dev/null
+++ b/t/t4018/php-interface.ctx
@@ -0,0 +1 @@
+interface RIGHT
diff --git a/t/t4018/php-method.ctx b/t/t4018/php-method.ctx
new file mode 100644
index 0000000000..eb1659ff9f
--- /dev/null
+++ b/t/t4018/php-method.ctx
@@ -0,0 +1 @@
+public static function RIGHT()
diff --git a/t/t4018/php-trait.ctx b/t/t4018/php-trait.ctx
new file mode 100644
index 0000000000..57aa4c6267
--- /dev/null
+++ b/t/t4018/php-trait.ctx
@@ -0,0 +1 @@
+trait RIGHT
diff --git a/t/t4018/python-async-def.ctx b/t/t4018/python-async-def.ctx
new file mode 100644
index 0000000000..468c548bbe
--- /dev/null
+++ b/t/t4018/python-async-def.ctx
@@ -0,0 +1 @@
+async def RIGHT(pi: int = 3.14):
diff --git a/t/t4018/python-class.ctx b/t/t4018/python-class.ctx
new file mode 100644
index 0000000000..a40b755e29
--- /dev/null
+++ b/t/t4018/python-class.ctx
@@ -0,0 +1 @@
+class RIGHT(int, str):
diff --git a/t/t4018/python-def.ctx b/t/t4018/python-def.ctx
new file mode 100644
index 0000000000..a1a9cbad63
--- /dev/null
+++ b/t/t4018/python-def.ctx
@@ -0,0 +1 @@
+def RIGHT(pi: int = 3.14):
diff --git a/t/t4018/python-indented-async-def.ctx b/t/t4018/python-indented-async-def.ctx
new file mode 100644
index 0000000000..d393620a1e
--- /dev/null
+++ b/t/t4018/python-indented-async-def.ctx
@@ -0,0 +1 @@
+async def RIGHT(self, x: int):
diff --git a/t/t4018/python-indented-class.ctx b/t/t4018/python-indented-class.ctx
new file mode 100644
index 0000000000..0881c84dba
--- /dev/null
+++ b/t/t4018/python-indented-class.ctx
@@ -0,0 +1 @@
+class RIGHT:
diff --git a/t/t4018/python-indented-def.ctx b/t/t4018/python-indented-def.ctx
new file mode 100644
index 0000000000..6e5a44b391
--- /dev/null
+++ b/t/t4018/python-indented-def.ctx
@@ -0,0 +1 @@
+def RIGHT(self, x: int):
diff --git a/t/t4018/rust-fn.ctx b/t/t4018/rust-fn.ctx
new file mode 100644
index 0000000000..baa37cf253
--- /dev/null
+++ b/t/t4018/rust-fn.ctx
@@ -0,0 +1 @@
+pub(self) fn RIGHT<T>(x: &[T]) where T: Debug {
diff --git a/t/t4018/rust-impl.ctx b/t/t4018/rust-impl.ctx
new file mode 100644
index 0000000000..5344c35f3f
--- /dev/null
+++ b/t/t4018/rust-impl.ctx
@@ -0,0 +1 @@
+impl<'a, T: AsRef<[u8]>>  std::RIGHT for Git<'a> {
diff --git a/t/t4018/rust-macro-rules.ctx b/t/t4018/rust-macro-rules.ctx
new file mode 100644
index 0000000000..7520463aa0
--- /dev/null
+++ b/t/t4018/rust-macro-rules.ctx
@@ -0,0 +1 @@
+macro_rules! RIGHT {
diff --git a/t/t4018/rust-struct.ctx b/t/t4018/rust-struct.ctx
new file mode 100644
index 0000000000..c1e09dc808
--- /dev/null
+++ b/t/t4018/rust-struct.ctx
@@ -0,0 +1 @@
+pub(super) struct RIGHT<'a> {
diff --git a/t/t4018/rust-trait.ctx b/t/t4018/rust-trait.ctx
new file mode 100644
index 0000000000..6af803db29
--- /dev/null
+++ b/t/t4018/rust-trait.ctx
@@ -0,0 +1 @@
+unsafe trait RIGHT<T> {
-- 
2.30.0.284.gd98b1dd5eaa7


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

* [PATCH 08/20] userdiff tests: rewrite hunk header test infrastructure
  2021-02-14 18:25     ` Ævar Arnfjörð Bjarmason
                         ` (7 preceding siblings ...)
  2021-02-15  0:52       ` [PATCH 07/20] userdiff tests: match full hunk headers Ævar Arnfjörð Bjarmason
@ 2021-02-15  0:52       ` Ævar Arnfjörð Bjarmason
  2021-02-15  0:52       ` [PATCH 09/20] blame tests: don't rely on t/t4018/ directory Ævar Arnfjörð Bjarmason
                         ` (12 subsequent siblings)
  21 siblings, 0 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15  0:52 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers,
	Ævar Arnfjörð Bjarmason

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 <avarab@gmail.com>
---
 t/t4018-diff-funcname.sh                      |  59 +++--
 t/t4018/README                                |  15 --
 t/t4018/README.ctx                            |   1 -
 t/t4018/bash-arithmetic-function              |   4 -
 t/t4018/bash-arithmetic-function.ctx          |   1 -
 t/t4018/bash-bashism-style-compact            |   6 -
 t/t4018/bash-bashism-style-compact.ctx        |   1 -
 t/t4018/bash-bashism-style-function           |   4 -
 t/t4018/bash-bashism-style-function.ctx       |   1 -
 t/t4018/bash-bashism-style-whitespace         |   4 -
 t/t4018/bash-bashism-style-whitespace.ctx     |   1 -
 t/t4018/bash-conditional-function             |   4 -
 t/t4018/bash-conditional-function.ctx         |   1 -
 t/t4018/bash-missing-parentheses              |   6 -
 t/t4018/bash-missing-parentheses.ctx          |   1 -
 t/t4018/bash-mixed-style-compact              |   4 -
 t/t4018/bash-mixed-style-compact.ctx          |   1 -
 t/t4018/bash-mixed-style-function             |   4 -
 t/t4018/bash-mixed-style-function.ctx         |   1 -
 t/t4018/bash-nested-functions                 |   6 -
 t/t4018/bash-nested-functions.ctx             |   1 -
 t/t4018/bash-other-characters                 |   4 -
 t/t4018/bash-other-characters.ctx             |   1 -
 t/t4018/bash-posix-style-compact              |   4 -
 t/t4018/bash-posix-style-compact.ctx          |   1 -
 t/t4018/bash-posix-style-function             |   4 -
 t/t4018/bash-posix-style-function.ctx         |   1 -
 t/t4018/bash-posix-style-whitespace           |   4 -
 t/t4018/bash-posix-style-whitespace.ctx       |   1 -
 t/t4018/bash-subshell-function                |   4 -
 t/t4018/bash-subshell-function.ctx            |   1 -
 t/t4018/bash-trailing-comment                 |   4 -
 t/t4018/bash-trailing-comment.ctx             |   1 -
 t/t4018/bash.sh                               | 160 ++++++++++++
 t/t4018/cpp-c++-function                      |   4 -
 t/t4018/cpp-c++-function.ctx                  |   1 -
 t/t4018/cpp-class-constructor                 |   4 -
 t/t4018/cpp-class-constructor-mem-init        |   5 -
 t/t4018/cpp-class-constructor-mem-init.ctx    |   1 -
 t/t4018/cpp-class-constructor.ctx             |   1 -
 t/t4018/cpp-class-definition                  |   4 -
 t/t4018/cpp-class-definition-derived          |   5 -
 t/t4018/cpp-class-definition-derived.ctx      |   1 -
 t/t4018/cpp-class-definition.ctx              |   1 -
 t/t4018/cpp-class-destructor                  |   4 -
 t/t4018/cpp-class-destructor.ctx              |   1 -
 t/t4018/cpp-function-returning-global-type    |   4 -
 .../cpp-function-returning-global-type.ctx    |   1 -
 t/t4018/cpp-function-returning-nested         |   5 -
 t/t4018/cpp-function-returning-nested.ctx     |   1 -
 t/t4018/cpp-function-returning-pointer        |   4 -
 t/t4018/cpp-function-returning-pointer.ctx    |   1 -
 t/t4018/cpp-function-returning-reference      |   4 -
 t/t4018/cpp-function-returning-reference.ctx  |   1 -
 t/t4018/cpp-gnu-style-function                |   5 -
 t/t4018/cpp-gnu-style-function.ctx            |   1 -
 t/t4018/cpp-namespace-definition              |   4 -
 t/t4018/cpp-namespace-definition.ctx          |   1 -
 t/t4018/cpp-operator-definition               |   4 -
 t/t4018/cpp-operator-definition.ctx           |   1 -
 t/t4018/cpp-skip-access-specifiers            |   8 -
 t/t4018/cpp-skip-access-specifiers.ctx        |   1 -
 t/t4018/cpp-skip-comment-block                |   9 -
 t/t4018/cpp-skip-comment-block.ctx            |   1 -
 t/t4018/cpp-skip-labels                       |   8 -
 t/t4018/cpp-skip-labels.ctx                   |   1 -
 t/t4018/cpp-struct-definition                 |   9 -
 t/t4018/cpp-struct-definition.ctx             |   1 -
 t/t4018/cpp-struct-single-line                |   7 -
 t/t4018/cpp-struct-single-line.ctx            |   1 -
 t/t4018/cpp-template-function-definition      |   4 -
 t/t4018/cpp-template-function-definition.ctx  |   1 -
 t/t4018/cpp-union-definition                  |   4 -
 t/t4018/cpp-union-definition.ctx              |   1 -
 t/t4018/cpp-void-c-function                   |   4 -
 t/t4018/cpp-void-c-function.ctx               |   1 -
 t/t4018/cpp.sh                                | 239 ++++++++++++++++++
 t/t4018/css-attribute-value-selector          |   4 -
 t/t4018/css-attribute-value-selector.ctx      |   1 -
 t/t4018/css-block-level-@-statements          |  10 -
 t/t4018/css-block-level-@-statements.ctx      |   1 -
 t/t4018/css-brace-in-col-1                    |   5 -
 t/t4018/css-brace-in-col-1.ctx                |   1 -
 t/t4018/css-class-selector                    |   4 -
 t/t4018/css-class-selector.ctx                |   1 -
 t/t4018/css-colon-eol                         |   4 -
 t/t4018/css-colon-eol.ctx                     |   1 -
 t/t4018/css-colon-selector                    |   5 -
 t/t4018/css-colon-selector.ctx                |   1 -
 t/t4018/css-common                            |   4 -
 t/t4018/css-common.ctx                        |   1 -
 t/t4018/css-id-selector                       |   4 -
 t/t4018/css-id-selector.ctx                   |   1 -
 t/t4018/css-long-selector-list                |   6 -
 t/t4018/css-long-selector-list.ctx            |   1 -
 t/t4018/css-prop-sans-indent                  |   5 -
 t/t4018/css-prop-sans-indent.ctx              |   1 -
 t/t4018/css-root-selector                     |   4 -
 t/t4018/css-root-selector.ctx                 |   1 -
 t/t4018/css-short-selector-list               |   4 -
 t/t4018/css-short-selector-list.ctx           |   1 -
 t/t4018/css-trailing-space                    |   5 -
 t/t4018/css-trailing-space.ctx                |   1 -
 t/t4018/css.sh                                | 146 +++++++++++
 t/t4018/custom1-pattern.ctx                   |   1 -
 t/t4018/{custom1-pattern => custom1.sh}       |  10 +
 t/t4018/custom2-match-to-end-of-line          |   8 -
 t/t4018/custom2-match-to-end-of-line.ctx      |   1 -
 t/t4018/custom2.sh                            |  18 ++
 t/t4018/custom3-alternation-in-pattern.ctx    |   1 -
 ...tom3-alternation-in-pattern => custom3.sh} |  10 +
 t/t4018/dts-labels                            |   9 -
 t/t4018/dts-labels.ctx                        |   1 -
 t/t4018/dts-node-unitless                     |   8 -
 t/t4018/dts-node-unitless.ctx                 |   1 -
 t/t4018/dts-nodes                             |   8 -
 t/t4018/dts-nodes-boolean-prop                |   9 -
 t/t4018/dts-nodes-boolean-prop.ctx            |   1 -
 t/t4018/dts-nodes-comment1                    |   8 -
 t/t4018/dts-nodes-comment1.ctx                |   1 -
 t/t4018/dts-nodes-comment2                    |   8 -
 t/t4018/dts-nodes-comment2.ctx                |   1 -
 t/t4018/dts-nodes-multiline-prop              |  13 -
 t/t4018/dts-nodes-multiline-prop.ctx          |   1 -
 t/t4018/dts-nodes.ctx                         |   1 -
 t/t4018/dts-reference                         |   9 -
 t/t4018/dts-reference.ctx                     |   1 -
 t/t4018/dts-root                              |   5 -
 t/t4018/dts-root-comment                      |   8 -
 t/t4018/dts-root-comment.ctx                  |   1 -
 t/t4018/dts-root.ctx                          |   1 -
 t/t4018/dts.sh                                | 149 +++++++++++
 t/t4018/elixir-do-not-pick-end                |   5 -
 t/t4018/elixir-do-not-pick-end.ctx            |   1 -
 t/t4018/elixir-ex-unit-test                   |   6 -
 t/t4018/elixir-ex-unit-test.ctx               |   1 -
 t/t4018/elixir-function                       |   5 -
 t/t4018/elixir-function.ctx                   |   1 -
 t/t4018/elixir-macro                          |   5 -
 t/t4018/elixir-macro.ctx                      |   1 -
 t/t4018/elixir-module                         |   9 -
 t/t4018/elixir-module-func                    |   8 -
 t/t4018/elixir-module-func.ctx                |   1 -
 t/t4018/elixir-module.ctx                     |   1 -
 t/t4018/elixir-nested-module                  |   9 -
 t/t4018/elixir-nested-module.ctx              |   1 -
 t/t4018/elixir-private-function               |   5 -
 t/t4018/elixir-private-function.ctx           |   1 -
 t/t4018/elixir-protocol                       |   6 -
 t/t4018/elixir-protocol-implementation        |   5 -
 t/t4018/elixir-protocol-implementation.ctx    |   1 -
 t/t4018/elixir-protocol.ctx                   |   1 -
 t/t4018/elixir.sh                             | 127 ++++++++++
 t/t4018/fortran-block-data                    |   5 -
 t/t4018/fortran-block-data.ctx                |   1 -
 t/t4018/fortran-comment                       |  13 -
 t/t4018/fortran-comment-keyword               |  14 -
 t/t4018/fortran-comment-keyword.ctx           |   1 -
 t/t4018/fortran-comment-legacy                |  13 -
 t/t4018/fortran-comment-legacy-star           |  13 -
 t/t4018/fortran-comment-legacy-star.ctx       |   1 -
 t/t4018/fortran-comment-legacy.ctx            |   1 -
 t/t4018/fortran-comment.ctx                   |   1 -
 t/t4018/fortran-external-function             |   9 -
 t/t4018/fortran-external-function.ctx         |   1 -
 t/t4018/fortran-external-subroutine           |   5 -
 t/t4018/fortran-external-subroutine.ctx       |   1 -
 t/t4018/fortran-module                        |   5 -
 t/t4018/fortran-module-procedure              |  13 -
 t/t4018/fortran-module-procedure.ctx          |   1 -
 t/t4018/fortran-module.ctx                    |   1 -
 t/t4018/fortran-program                       |   5 -
 t/t4018/fortran-program.ctx                   |   1 -
 t/t4018/fortran.sh                            | 159 ++++++++++++
 t/t4018/fountain-scene                        |   4 -
 t/t4018/fountain-scene.ctx                    |   1 -
 t/t4018/fountain.sh                           |  14 +
 t/t4018/golang-complex-function               |   8 -
 t/t4018/golang-complex-function.ctx           |   1 -
 t/t4018/golang-func                           |   4 -
 t/t4018/golang-func.ctx                       |   1 -
 t/t4018/golang-interface                      |   4 -
 t/t4018/golang-interface.ctx                  |   1 -
 t/t4018/golang-long-func                      |   5 -
 t/t4018/golang-long-func.ctx                  |   1 -
 t/t4018/golang-struct                         |   4 -
 t/t4018/golang-struct.ctx                     |   1 -
 t/t4018/golang.sh                             |  59 +++++
 t/t4018/java-class-member-function            |   8 -
 t/t4018/java-class-member-function.ctx        |   1 -
 t/t4018/java.sh                               |  18 ++
 t/t4018/markdown-heading-indented             |   6 -
 t/t4018/markdown-heading-indented.ctx         |   1 -
 t/t4018/markdown-heading-non-headings         |  17 --
 t/t4018/markdown-heading-non-headings.ctx     |   1 -
 t/t4018/markdown.sh                           |  39 +++
 t/t4018/matlab-class-definition               |   5 -
 t/t4018/matlab-class-definition.ctx           |   1 -
 t/t4018/matlab-function                       |   4 -
 t/t4018/matlab-function.ctx                   |   1 -
 t/t4018/matlab-octave-section-1               |   3 -
 t/t4018/matlab-octave-section-1.ctx           |   1 -
 t/t4018/matlab-octave-section-2               |   3 -
 t/t4018/matlab-octave-section-2.ctx           |   1 -
 t/t4018/matlab-section                        |   3 -
 t/t4018/matlab-section.ctx                    |   1 -
 t/t4018/matlab.sh                             |  52 ++++
 t/t4018/perl-skip-end-of-heredoc              |   8 -
 t/t4018/perl-skip-end-of-heredoc.ctx          |   1 -
 t/t4018/perl-skip-forward-decl                |  10 -
 t/t4018/perl-skip-forward-decl.ctx            |   1 -
 t/t4018/perl-skip-sub-in-pod                  |  18 --
 t/t4018/perl-skip-sub-in-pod.ctx              |   1 -
 t/t4018/perl-sub-definition                   |   4 -
 t/t4018/perl-sub-definition-kr-brace          |   4 -
 t/t4018/perl-sub-definition-kr-brace.ctx      |   1 -
 t/t4018/perl-sub-definition.ctx               |   1 -
 t/t4018/perl.sh                               |  78 ++++++
 t/t4018/php-abstract-class                    |   4 -
 t/t4018/php-abstract-class.ctx                |   1 -
 t/t4018/php-abstract-method                   |   7 -
 t/t4018/php-abstract-method.ctx               |   1 -
 t/t4018/php-class                             |   4 -
 t/t4018/php-class.ctx                         |   1 -
 t/t4018/php-final-class                       |   4 -
 t/t4018/php-final-class.ctx                   |   1 -
 t/t4018/php-final-method                      |   7 -
 t/t4018/php-final-method.ctx                  |   1 -
 t/t4018/php-function                          |   4 -
 t/t4018/php-function.ctx                      |   1 -
 t/t4018/php-interface                         |   4 -
 t/t4018/php-interface.ctx                     |   1 -
 t/t4018/php-method                            |   7 -
 t/t4018/php-method.ctx                        |   1 -
 t/t4018/php-trait                             |   7 -
 t/t4018/php-trait.ctx                         |   1 -
 t/t4018/php.sh                                | 106 ++++++++
 t/t4018/python-async-def                      |   4 -
 t/t4018/python-async-def.ctx                  |   1 -
 t/t4018/python-class                          |   4 -
 t/t4018/python-class.ctx                      |   1 -
 t/t4018/python-def                            |   4 -
 t/t4018/python-def.ctx                        |   1 -
 t/t4018/python-indented-async-def             |   7 -
 t/t4018/python-indented-async-def.ctx         |   1 -
 t/t4018/python-indented-class                 |   5 -
 t/t4018/python-indented-class.ctx             |   1 -
 t/t4018/python-indented-def                   |   7 -
 t/t4018/python-indented-def.ctx               |   1 -
 t/t4018/python.sh                             |  71 ++++++
 t/t4018/rust-fn                               |   5 -
 t/t4018/rust-fn.ctx                           |   1 -
 t/t4018/rust-impl                             |   5 -
 t/t4018/rust-impl.ctx                         |   1 -
 t/t4018/rust-macro-rules                      |   6 -
 t/t4018/rust-macro-rules.ctx                  |   1 -
 t/t4018/rust-struct                           |   5 -
 t/t4018/rust-struct.ctx                       |   1 -
 t/t4018/rust-trait                            |   5 -
 t/t4018/rust-trait.ctx                        |   1 -
 t/t4018/rust.sh                               |  60 +++++
 261 files changed, 1549 insertions(+), 879 deletions(-)
 delete mode 100644 t/t4018/README
 delete mode 100644 t/t4018/README.ctx
 delete mode 100644 t/t4018/bash-arithmetic-function
 delete mode 100644 t/t4018/bash-arithmetic-function.ctx
 delete mode 100644 t/t4018/bash-bashism-style-compact
 delete mode 100644 t/t4018/bash-bashism-style-compact.ctx
 delete mode 100644 t/t4018/bash-bashism-style-function
 delete mode 100644 t/t4018/bash-bashism-style-function.ctx
 delete mode 100644 t/t4018/bash-bashism-style-whitespace
 delete mode 100644 t/t4018/bash-bashism-style-whitespace.ctx
 delete mode 100644 t/t4018/bash-conditional-function
 delete mode 100644 t/t4018/bash-conditional-function.ctx
 delete mode 100644 t/t4018/bash-missing-parentheses
 delete mode 100644 t/t4018/bash-missing-parentheses.ctx
 delete mode 100644 t/t4018/bash-mixed-style-compact
 delete mode 100644 t/t4018/bash-mixed-style-compact.ctx
 delete mode 100644 t/t4018/bash-mixed-style-function
 delete mode 100644 t/t4018/bash-mixed-style-function.ctx
 delete mode 100644 t/t4018/bash-nested-functions
 delete mode 100644 t/t4018/bash-nested-functions.ctx
 delete mode 100644 t/t4018/bash-other-characters
 delete mode 100644 t/t4018/bash-other-characters.ctx
 delete mode 100644 t/t4018/bash-posix-style-compact
 delete mode 100644 t/t4018/bash-posix-style-compact.ctx
 delete mode 100644 t/t4018/bash-posix-style-function
 delete mode 100644 t/t4018/bash-posix-style-function.ctx
 delete mode 100644 t/t4018/bash-posix-style-whitespace
 delete mode 100644 t/t4018/bash-posix-style-whitespace.ctx
 delete mode 100644 t/t4018/bash-subshell-function
 delete mode 100644 t/t4018/bash-subshell-function.ctx
 delete mode 100644 t/t4018/bash-trailing-comment
 delete mode 100644 t/t4018/bash-trailing-comment.ctx
 create mode 100755 t/t4018/bash.sh
 delete mode 100644 t/t4018/cpp-c++-function
 delete mode 100644 t/t4018/cpp-c++-function.ctx
 delete mode 100644 t/t4018/cpp-class-constructor
 delete mode 100644 t/t4018/cpp-class-constructor-mem-init
 delete mode 100644 t/t4018/cpp-class-constructor-mem-init.ctx
 delete mode 100644 t/t4018/cpp-class-constructor.ctx
 delete mode 100644 t/t4018/cpp-class-definition
 delete mode 100644 t/t4018/cpp-class-definition-derived
 delete mode 100644 t/t4018/cpp-class-definition-derived.ctx
 delete mode 100644 t/t4018/cpp-class-definition.ctx
 delete mode 100644 t/t4018/cpp-class-destructor
 delete mode 100644 t/t4018/cpp-class-destructor.ctx
 delete mode 100644 t/t4018/cpp-function-returning-global-type
 delete mode 100644 t/t4018/cpp-function-returning-global-type.ctx
 delete mode 100644 t/t4018/cpp-function-returning-nested
 delete mode 100644 t/t4018/cpp-function-returning-nested.ctx
 delete mode 100644 t/t4018/cpp-function-returning-pointer
 delete mode 100644 t/t4018/cpp-function-returning-pointer.ctx
 delete mode 100644 t/t4018/cpp-function-returning-reference
 delete mode 100644 t/t4018/cpp-function-returning-reference.ctx
 delete mode 100644 t/t4018/cpp-gnu-style-function
 delete mode 100644 t/t4018/cpp-gnu-style-function.ctx
 delete mode 100644 t/t4018/cpp-namespace-definition
 delete mode 100644 t/t4018/cpp-namespace-definition.ctx
 delete mode 100644 t/t4018/cpp-operator-definition
 delete mode 100644 t/t4018/cpp-operator-definition.ctx
 delete mode 100644 t/t4018/cpp-skip-access-specifiers
 delete mode 100644 t/t4018/cpp-skip-access-specifiers.ctx
 delete mode 100644 t/t4018/cpp-skip-comment-block
 delete mode 100644 t/t4018/cpp-skip-comment-block.ctx
 delete mode 100644 t/t4018/cpp-skip-labels
 delete mode 100644 t/t4018/cpp-skip-labels.ctx
 delete mode 100644 t/t4018/cpp-struct-definition
 delete mode 100644 t/t4018/cpp-struct-definition.ctx
 delete mode 100644 t/t4018/cpp-struct-single-line
 delete mode 100644 t/t4018/cpp-struct-single-line.ctx
 delete mode 100644 t/t4018/cpp-template-function-definition
 delete mode 100644 t/t4018/cpp-template-function-definition.ctx
 delete mode 100644 t/t4018/cpp-union-definition
 delete mode 100644 t/t4018/cpp-union-definition.ctx
 delete mode 100644 t/t4018/cpp-void-c-function
 delete mode 100644 t/t4018/cpp-void-c-function.ctx
 create mode 100755 t/t4018/cpp.sh
 delete mode 100644 t/t4018/css-attribute-value-selector
 delete mode 100644 t/t4018/css-attribute-value-selector.ctx
 delete mode 100644 t/t4018/css-block-level-@-statements
 delete mode 100644 t/t4018/css-block-level-@-statements.ctx
 delete mode 100644 t/t4018/css-brace-in-col-1
 delete mode 100644 t/t4018/css-brace-in-col-1.ctx
 delete mode 100644 t/t4018/css-class-selector
 delete mode 100644 t/t4018/css-class-selector.ctx
 delete mode 100644 t/t4018/css-colon-eol
 delete mode 100644 t/t4018/css-colon-eol.ctx
 delete mode 100644 t/t4018/css-colon-selector
 delete mode 100644 t/t4018/css-colon-selector.ctx
 delete mode 100644 t/t4018/css-common
 delete mode 100644 t/t4018/css-common.ctx
 delete mode 100644 t/t4018/css-id-selector
 delete mode 100644 t/t4018/css-id-selector.ctx
 delete mode 100644 t/t4018/css-long-selector-list
 delete mode 100644 t/t4018/css-long-selector-list.ctx
 delete mode 100644 t/t4018/css-prop-sans-indent
 delete mode 100644 t/t4018/css-prop-sans-indent.ctx
 delete mode 100644 t/t4018/css-root-selector
 delete mode 100644 t/t4018/css-root-selector.ctx
 delete mode 100644 t/t4018/css-short-selector-list
 delete mode 100644 t/t4018/css-short-selector-list.ctx
 delete mode 100644 t/t4018/css-trailing-space
 delete mode 100644 t/t4018/css-trailing-space.ctx
 create mode 100755 t/t4018/css.sh
 delete mode 100644 t/t4018/custom1-pattern.ctx
 rename t/t4018/{custom1-pattern => custom1.sh} (71%)
 mode change 100644 => 100755
 delete mode 100644 t/t4018/custom2-match-to-end-of-line
 delete mode 100644 t/t4018/custom2-match-to-end-of-line.ctx
 create mode 100755 t/t4018/custom2.sh
 delete mode 100644 t/t4018/custom3-alternation-in-pattern.ctx
 rename t/t4018/{custom3-alternation-in-pattern => custom3.sh} (66%)
 mode change 100644 => 100755
 delete mode 100644 t/t4018/dts-labels
 delete mode 100644 t/t4018/dts-labels.ctx
 delete mode 100644 t/t4018/dts-node-unitless
 delete mode 100644 t/t4018/dts-node-unitless.ctx
 delete mode 100644 t/t4018/dts-nodes
 delete mode 100644 t/t4018/dts-nodes-boolean-prop
 delete mode 100644 t/t4018/dts-nodes-boolean-prop.ctx
 delete mode 100644 t/t4018/dts-nodes-comment1
 delete mode 100644 t/t4018/dts-nodes-comment1.ctx
 delete mode 100644 t/t4018/dts-nodes-comment2
 delete mode 100644 t/t4018/dts-nodes-comment2.ctx
 delete mode 100644 t/t4018/dts-nodes-multiline-prop
 delete mode 100644 t/t4018/dts-nodes-multiline-prop.ctx
 delete mode 100644 t/t4018/dts-nodes.ctx
 delete mode 100644 t/t4018/dts-reference
 delete mode 100644 t/t4018/dts-reference.ctx
 delete mode 100644 t/t4018/dts-root
 delete mode 100644 t/t4018/dts-root-comment
 delete mode 100644 t/t4018/dts-root-comment.ctx
 delete mode 100644 t/t4018/dts-root.ctx
 create mode 100755 t/t4018/dts.sh
 delete mode 100644 t/t4018/elixir-do-not-pick-end
 delete mode 100644 t/t4018/elixir-do-not-pick-end.ctx
 delete mode 100644 t/t4018/elixir-ex-unit-test
 delete mode 100644 t/t4018/elixir-ex-unit-test.ctx
 delete mode 100644 t/t4018/elixir-function
 delete mode 100644 t/t4018/elixir-function.ctx
 delete mode 100644 t/t4018/elixir-macro
 delete mode 100644 t/t4018/elixir-macro.ctx
 delete mode 100644 t/t4018/elixir-module
 delete mode 100644 t/t4018/elixir-module-func
 delete mode 100644 t/t4018/elixir-module-func.ctx
 delete mode 100644 t/t4018/elixir-module.ctx
 delete mode 100644 t/t4018/elixir-nested-module
 delete mode 100644 t/t4018/elixir-nested-module.ctx
 delete mode 100644 t/t4018/elixir-private-function
 delete mode 100644 t/t4018/elixir-private-function.ctx
 delete mode 100644 t/t4018/elixir-protocol
 delete mode 100644 t/t4018/elixir-protocol-implementation
 delete mode 100644 t/t4018/elixir-protocol-implementation.ctx
 delete mode 100644 t/t4018/elixir-protocol.ctx
 create mode 100755 t/t4018/elixir.sh
 delete mode 100644 t/t4018/fortran-block-data
 delete mode 100644 t/t4018/fortran-block-data.ctx
 delete mode 100644 t/t4018/fortran-comment
 delete mode 100644 t/t4018/fortran-comment-keyword
 delete mode 100644 t/t4018/fortran-comment-keyword.ctx
 delete mode 100644 t/t4018/fortran-comment-legacy
 delete mode 100644 t/t4018/fortran-comment-legacy-star
 delete mode 100644 t/t4018/fortran-comment-legacy-star.ctx
 delete mode 100644 t/t4018/fortran-comment-legacy.ctx
 delete mode 100644 t/t4018/fortran-comment.ctx
 delete mode 100644 t/t4018/fortran-external-function
 delete mode 100644 t/t4018/fortran-external-function.ctx
 delete mode 100644 t/t4018/fortran-external-subroutine
 delete mode 100644 t/t4018/fortran-external-subroutine.ctx
 delete mode 100644 t/t4018/fortran-module
 delete mode 100644 t/t4018/fortran-module-procedure
 delete mode 100644 t/t4018/fortran-module-procedure.ctx
 delete mode 100644 t/t4018/fortran-module.ctx
 delete mode 100644 t/t4018/fortran-program
 delete mode 100644 t/t4018/fortran-program.ctx
 create mode 100755 t/t4018/fortran.sh
 delete mode 100644 t/t4018/fountain-scene
 delete mode 100644 t/t4018/fountain-scene.ctx
 create mode 100755 t/t4018/fountain.sh
 delete mode 100644 t/t4018/golang-complex-function
 delete mode 100644 t/t4018/golang-complex-function.ctx
 delete mode 100644 t/t4018/golang-func
 delete mode 100644 t/t4018/golang-func.ctx
 delete mode 100644 t/t4018/golang-interface
 delete mode 100644 t/t4018/golang-interface.ctx
 delete mode 100644 t/t4018/golang-long-func
 delete mode 100644 t/t4018/golang-long-func.ctx
 delete mode 100644 t/t4018/golang-struct
 delete mode 100644 t/t4018/golang-struct.ctx
 create mode 100755 t/t4018/golang.sh
 delete mode 100644 t/t4018/java-class-member-function
 delete mode 100644 t/t4018/java-class-member-function.ctx
 create mode 100755 t/t4018/java.sh
 delete mode 100644 t/t4018/markdown-heading-indented
 delete mode 100644 t/t4018/markdown-heading-indented.ctx
 delete mode 100644 t/t4018/markdown-heading-non-headings
 delete mode 100644 t/t4018/markdown-heading-non-headings.ctx
 create mode 100755 t/t4018/markdown.sh
 delete mode 100644 t/t4018/matlab-class-definition
 delete mode 100644 t/t4018/matlab-class-definition.ctx
 delete mode 100644 t/t4018/matlab-function
 delete mode 100644 t/t4018/matlab-function.ctx
 delete mode 100644 t/t4018/matlab-octave-section-1
 delete mode 100644 t/t4018/matlab-octave-section-1.ctx
 delete mode 100644 t/t4018/matlab-octave-section-2
 delete mode 100644 t/t4018/matlab-octave-section-2.ctx
 delete mode 100644 t/t4018/matlab-section
 delete mode 100644 t/t4018/matlab-section.ctx
 create mode 100755 t/t4018/matlab.sh
 delete mode 100644 t/t4018/perl-skip-end-of-heredoc
 delete mode 100644 t/t4018/perl-skip-end-of-heredoc.ctx
 delete mode 100644 t/t4018/perl-skip-forward-decl
 delete mode 100644 t/t4018/perl-skip-forward-decl.ctx
 delete mode 100644 t/t4018/perl-skip-sub-in-pod
 delete mode 100644 t/t4018/perl-skip-sub-in-pod.ctx
 delete mode 100644 t/t4018/perl-sub-definition
 delete mode 100644 t/t4018/perl-sub-definition-kr-brace
 delete mode 100644 t/t4018/perl-sub-definition-kr-brace.ctx
 delete mode 100644 t/t4018/perl-sub-definition.ctx
 create mode 100755 t/t4018/perl.sh
 delete mode 100644 t/t4018/php-abstract-class
 delete mode 100644 t/t4018/php-abstract-class.ctx
 delete mode 100644 t/t4018/php-abstract-method
 delete mode 100644 t/t4018/php-abstract-method.ctx
 delete mode 100644 t/t4018/php-class
 delete mode 100644 t/t4018/php-class.ctx
 delete mode 100644 t/t4018/php-final-class
 delete mode 100644 t/t4018/php-final-class.ctx
 delete mode 100644 t/t4018/php-final-method
 delete mode 100644 t/t4018/php-final-method.ctx
 delete mode 100644 t/t4018/php-function
 delete mode 100644 t/t4018/php-function.ctx
 delete mode 100644 t/t4018/php-interface
 delete mode 100644 t/t4018/php-interface.ctx
 delete mode 100644 t/t4018/php-method
 delete mode 100644 t/t4018/php-method.ctx
 delete mode 100644 t/t4018/php-trait
 delete mode 100644 t/t4018/php-trait.ctx
 create mode 100755 t/t4018/php.sh
 delete mode 100644 t/t4018/python-async-def
 delete mode 100644 t/t4018/python-async-def.ctx
 delete mode 100644 t/t4018/python-class
 delete mode 100644 t/t4018/python-class.ctx
 delete mode 100644 t/t4018/python-def
 delete mode 100644 t/t4018/python-def.ctx
 delete mode 100644 t/t4018/python-indented-async-def
 delete mode 100644 t/t4018/python-indented-async-def.ctx
 delete mode 100644 t/t4018/python-indented-class
 delete mode 100644 t/t4018/python-indented-class.ctx
 delete mode 100644 t/t4018/python-indented-def
 delete mode 100644 t/t4018/python-indented-def.ctx
 create mode 100755 t/t4018/python.sh
 delete mode 100644 t/t4018/rust-fn
 delete mode 100644 t/t4018/rust-fn.ctx
 delete mode 100644 t/t4018/rust-impl
 delete mode 100644 t/t4018/rust-impl.ctx
 delete mode 100644 t/t4018/rust-macro-rules
 delete mode 100644 t/t4018/rust-macro-rules.ctx
 delete mode 100644 t/t4018/rust-struct
 delete mode 100644 t/t4018/rust-struct.ctx
 delete mode 100644 t/t4018/rust-trait
 delete mode 100644 t/t4018/rust-trait.ctx
 create mode 100755 t/t4018/rust.sh

diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index d32c38ad1a..3ff34c13d7 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -61,33 +61,42 @@ test_expect_success 'last regexp must not be negated' '
 	test_i18ngrep ": Last expression must not be negated:" msg
 '
 
-test_expect_success 'setup hunk header tests' '
-	for i in $diffpatterns
-	do
-		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)
-	do
-		sed -e "s/ChangeMe/IWasChanged/" <"$TEST_DIRECTORY/t4018/$i" >"$i" || return 1
-	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" &&
+		sed -e "s/ChangeMe/IWasChanged/" <"$what" >tmp &&
+		mv tmp "$what"
+	' &&
+
+	test_expect_success "$desc" '
+		git diff -U1 "$what" >diff &&
+		sed -n -e "s/^.*@@\( \|$\)//p" <diff >actual &&
+		test_cmp expected actual
+	'
+}
 
-# check each individual file
-for i in $(git ls-files -- ':!*.ctx')
+for what in $diffpatterns
 do
-	test_expect_success "hunk header: $i" "
-		git diff -U1 $i >diff &&
-		sed -n -e 's/^.*@@\( \|$\)//p' <diff >ctx &&
-		test_cmp $i.ctx ctx
-	"
+	test="$TEST_DIRECTORY/t4018/$what.sh"
+	if ! test -e "$test"
+	then
+		test_expect_failure "$what: no tests" 'false'
+		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
deleted file mode 100644
index 2cccf8c950..0000000000
--- a/t/t4018/README
+++ /dev/null
@@ -1,15 +0,0 @@
-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 contains the word
-"RIGHT" by convention. The "LANG-whatever.ctx" file contains what we
-expect to appear in the hunk header. We munged the start of the line
-to "@@ [...] @@" for ease of not having to hardcode the line numbers
-and offsets.
diff --git a/t/t4018/README.ctx b/t/t4018/README.ctx
deleted file mode 100644
index cd79384b04..0000000000
--- a/t/t4018/README.ctx
+++ /dev/null
@@ -1 +0,0 @@
-description of the test.
diff --git a/t/t4018/bash-arithmetic-function b/t/t4018/bash-arithmetic-function
deleted file mode 100644
index c0b276cb50..0000000000
--- a/t/t4018/bash-arithmetic-function
+++ /dev/null
@@ -1,4 +0,0 @@
-RIGHT() ((
-
-    ChangeMe = "$x" + "$y"
-))
diff --git a/t/t4018/bash-arithmetic-function.ctx b/t/t4018/bash-arithmetic-function.ctx
deleted file mode 100644
index 811eac7d2f..0000000000
--- a/t/t4018/bash-arithmetic-function.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT()
diff --git a/t/t4018/bash-bashism-style-compact b/t/t4018/bash-bashism-style-compact
deleted file mode 100644
index 1ca3126f61..0000000000
--- a/t/t4018/bash-bashism-style-compact
+++ /dev/null
@@ -1,6 +0,0 @@
-function RIGHT {
-    function InvalidSyntax{
-        :
-        echo 'ChangeMe'
-    }
-}
diff --git a/t/t4018/bash-bashism-style-compact.ctx b/t/t4018/bash-bashism-style-compact.ctx
deleted file mode 100644
index 4f8eac48c6..0000000000
--- a/t/t4018/bash-bashism-style-compact.ctx
+++ /dev/null
@@ -1 +0,0 @@
-function RIGHT {
diff --git a/t/t4018/bash-bashism-style-function b/t/t4018/bash-bashism-style-function
deleted file mode 100644
index f1de4fa831..0000000000
--- a/t/t4018/bash-bashism-style-function
+++ /dev/null
@@ -1,4 +0,0 @@
-function RIGHT {
-    :
-    echo 'ChangeMe'
-}
diff --git a/t/t4018/bash-bashism-style-function.ctx b/t/t4018/bash-bashism-style-function.ctx
deleted file mode 100644
index 4f8eac48c6..0000000000
--- a/t/t4018/bash-bashism-style-function.ctx
+++ /dev/null
@@ -1 +0,0 @@
-function RIGHT {
diff --git a/t/t4018/bash-bashism-style-whitespace b/t/t4018/bash-bashism-style-whitespace
deleted file mode 100644
index ade85dd3a5..0000000000
--- a/t/t4018/bash-bashism-style-whitespace
+++ /dev/null
@@ -1,4 +0,0 @@
-	 function 	RIGHT 	( 	) 	{
-
-	    ChangeMe
-	 }
diff --git a/t/t4018/bash-bashism-style-whitespace.ctx b/t/t4018/bash-bashism-style-whitespace.ctx
deleted file mode 100644
index 35dbd0220e..0000000000
--- a/t/t4018/bash-bashism-style-whitespace.ctx
+++ /dev/null
@@ -1 +0,0 @@
-function 	RIGHT 	( 	) 	{
diff --git a/t/t4018/bash-conditional-function b/t/t4018/bash-conditional-function
deleted file mode 100644
index c5949e829b..0000000000
--- a/t/t4018/bash-conditional-function
+++ /dev/null
@@ -1,4 +0,0 @@
-RIGHT() [[ \
-
-    "$a" > "$ChangeMe"
-]]
diff --git a/t/t4018/bash-conditional-function.ctx b/t/t4018/bash-conditional-function.ctx
deleted file mode 100644
index 811eac7d2f..0000000000
--- a/t/t4018/bash-conditional-function.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT()
diff --git a/t/t4018/bash-missing-parentheses b/t/t4018/bash-missing-parentheses
deleted file mode 100644
index 8c8a05dd7a..0000000000
--- a/t/t4018/bash-missing-parentheses
+++ /dev/null
@@ -1,6 +0,0 @@
-function RIGHT {
-    functionInvalidSyntax {
-        :
-        echo 'ChangeMe'
-    }
-}
diff --git a/t/t4018/bash-missing-parentheses.ctx b/t/t4018/bash-missing-parentheses.ctx
deleted file mode 100644
index 4f8eac48c6..0000000000
--- a/t/t4018/bash-missing-parentheses.ctx
+++ /dev/null
@@ -1 +0,0 @@
-function RIGHT {
diff --git a/t/t4018/bash-mixed-style-compact b/t/t4018/bash-mixed-style-compact
deleted file mode 100644
index d9364cba67..0000000000
--- a/t/t4018/bash-mixed-style-compact
+++ /dev/null
@@ -1,4 +0,0 @@
-function RIGHT(){
-    :
-    echo 'ChangeMe'
-}
diff --git a/t/t4018/bash-mixed-style-compact.ctx b/t/t4018/bash-mixed-style-compact.ctx
deleted file mode 100644
index bba11074eb..0000000000
--- a/t/t4018/bash-mixed-style-compact.ctx
+++ /dev/null
@@ -1 +0,0 @@
-function RIGHT(){
diff --git a/t/t4018/bash-mixed-style-function b/t/t4018/bash-mixed-style-function
deleted file mode 100644
index 555f9b2466..0000000000
--- a/t/t4018/bash-mixed-style-function
+++ /dev/null
@@ -1,4 +0,0 @@
-function RIGHT() {
-
-    ChangeMe
-}
diff --git a/t/t4018/bash-mixed-style-function.ctx b/t/t4018/bash-mixed-style-function.ctx
deleted file mode 100644
index 922b87a4aa..0000000000
--- a/t/t4018/bash-mixed-style-function.ctx
+++ /dev/null
@@ -1 +0,0 @@
-function RIGHT() {
diff --git a/t/t4018/bash-nested-functions b/t/t4018/bash-nested-functions
deleted file mode 100644
index 2c9237ead4..0000000000
--- a/t/t4018/bash-nested-functions
+++ /dev/null
@@ -1,6 +0,0 @@
-outer() {
-    RIGHT() {
-        :
-        echo 'ChangeMe'
-    }
-}
diff --git a/t/t4018/bash-nested-functions.ctx b/t/t4018/bash-nested-functions.ctx
deleted file mode 100644
index 811eac7d2f..0000000000
--- a/t/t4018/bash-nested-functions.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT()
diff --git a/t/t4018/bash-other-characters b/t/t4018/bash-other-characters
deleted file mode 100644
index a3f390d525..0000000000
--- a/t/t4018/bash-other-characters
+++ /dev/null
@@ -1,4 +0,0 @@
-_RIGHT_0n() {
-
-    ChangeMe
-}
diff --git a/t/t4018/bash-other-characters.ctx b/t/t4018/bash-other-characters.ctx
deleted file mode 100644
index 6a55317fdf..0000000000
--- a/t/t4018/bash-other-characters.ctx
+++ /dev/null
@@ -1 +0,0 @@
-_RIGHT_0n()
diff --git a/t/t4018/bash-posix-style-compact b/t/t4018/bash-posix-style-compact
deleted file mode 100644
index 045bd2029b..0000000000
--- a/t/t4018/bash-posix-style-compact
+++ /dev/null
@@ -1,4 +0,0 @@
-RIGHT(){
-
-    ChangeMe
-}
diff --git a/t/t4018/bash-posix-style-compact.ctx b/t/t4018/bash-posix-style-compact.ctx
deleted file mode 100644
index 811eac7d2f..0000000000
--- a/t/t4018/bash-posix-style-compact.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT()
diff --git a/t/t4018/bash-posix-style-function b/t/t4018/bash-posix-style-function
deleted file mode 100644
index a4d144856e..0000000000
--- a/t/t4018/bash-posix-style-function
+++ /dev/null
@@ -1,4 +0,0 @@
-RIGHT() {
-
-    ChangeMe
-}
diff --git a/t/t4018/bash-posix-style-function.ctx b/t/t4018/bash-posix-style-function.ctx
deleted file mode 100644
index 811eac7d2f..0000000000
--- a/t/t4018/bash-posix-style-function.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT()
diff --git a/t/t4018/bash-posix-style-whitespace b/t/t4018/bash-posix-style-whitespace
deleted file mode 100644
index 4d984f0aa4..0000000000
--- a/t/t4018/bash-posix-style-whitespace
+++ /dev/null
@@ -1,4 +0,0 @@
-	 RIGHT 	( 	) 	{
-
-	    ChangeMe
-	 }
diff --git a/t/t4018/bash-posix-style-whitespace.ctx b/t/t4018/bash-posix-style-whitespace.ctx
deleted file mode 100644
index 28f8698e14..0000000000
--- a/t/t4018/bash-posix-style-whitespace.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT 	( 	)
diff --git a/t/t4018/bash-subshell-function b/t/t4018/bash-subshell-function
deleted file mode 100644
index 80baa09484..0000000000
--- a/t/t4018/bash-subshell-function
+++ /dev/null
@@ -1,4 +0,0 @@
-RIGHT() (
-
-    ChangeMe=2
-)
diff --git a/t/t4018/bash-subshell-function.ctx b/t/t4018/bash-subshell-function.ctx
deleted file mode 100644
index 811eac7d2f..0000000000
--- a/t/t4018/bash-subshell-function.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT()
diff --git a/t/t4018/bash-trailing-comment b/t/t4018/bash-trailing-comment
deleted file mode 100644
index f1edbeda31..0000000000
--- a/t/t4018/bash-trailing-comment
+++ /dev/null
@@ -1,4 +0,0 @@
-RIGHT() { # Comment
-
-    ChangeMe
-}
diff --git a/t/t4018/bash-trailing-comment.ctx b/t/t4018/bash-trailing-comment.ctx
deleted file mode 100644
index 811eac7d2f..0000000000
--- a/t/t4018/bash-trailing-comment.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT()
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
+
+test_diff_funcname 'bash: bashism style whitespace' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+function 	RIGHT 	( 	) 	{
+EOF_HUNK
+	 function 	RIGHT 	( 	) 	{
+
+	    ChangeMe
+	 }
+EOF_TEST
+
+test_diff_funcname 'bash: conditional function' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT()
+EOF_HUNK
+RIGHT() [[ \
+
+    "$a" > "$ChangeMe"
+]]
+EOF_TEST
+
+test_diff_funcname 'bash: missing parentheses' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+function RIGHT {
+EOF_HUNK
+function RIGHT {
+    functionInvalidSyntax {
+        :
+        echo 'ChangeMe'
+    }
+}
+EOF_TEST
+
+test_diff_funcname 'bash: mixed style compact' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+function RIGHT(){
+EOF_HUNK
+function RIGHT(){
+    :
+    echo 'ChangeMe'
+}
+EOF_TEST
+
+test_diff_funcname 'bash: mixed style function' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+function RIGHT() {
+EOF_HUNK
+function RIGHT() {
+
+    ChangeMe
+}
+EOF_TEST
+
+test_diff_funcname 'bash: nested functions' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT()
+EOF_HUNK
+outer() {
+    RIGHT() {
+        :
+        echo 'ChangeMe'
+    }
+}
+EOF_TEST
+
+test_diff_funcname 'bash: other characters' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+_RIGHT_0n()
+EOF_HUNK
+_RIGHT_0n() {
+
+    ChangeMe
+}
+EOF_TEST
+
+test_diff_funcname 'bash: posix style compact' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT()
+EOF_HUNK
+RIGHT(){
+
+    ChangeMe
+}
+EOF_TEST
+
+test_diff_funcname 'bash: posix style function' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT()
+EOF_HUNK
+RIGHT() {
+
+    ChangeMe
+}
+EOF_TEST
+
+test_diff_funcname 'bash: posix style whitespace' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT 	( 	)
+EOF_HUNK
+	 RIGHT 	( 	) 	{
+
+	    ChangeMe
+	 }
+EOF_TEST
+
+test_diff_funcname 'bash: subshell function' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT()
+EOF_HUNK
+RIGHT() (
+
+    ChangeMe=2
+)
+EOF_TEST
+
+test_diff_funcname 'bash: trailing comment' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT()
+EOF_HUNK
+RIGHT() { # Comment
+
+    ChangeMe
+}
+EOF_TEST
diff --git a/t/t4018/cpp-c++-function b/t/t4018/cpp-c++-function
deleted file mode 100644
index 9ee6bbef55..0000000000
--- a/t/t4018/cpp-c++-function
+++ /dev/null
@@ -1,4 +0,0 @@
-Item RIGHT::DoSomething( Args with_spaces )
-{
-	ChangeMe;
-}
diff --git a/t/t4018/cpp-c++-function.ctx b/t/t4018/cpp-c++-function.ctx
deleted file mode 100644
index 337b49dbb3..0000000000
--- a/t/t4018/cpp-c++-function.ctx
+++ /dev/null
@@ -1 +0,0 @@
-Item RIGHT::DoSomething( Args with_spaces )
diff --git a/t/t4018/cpp-class-constructor b/t/t4018/cpp-class-constructor
deleted file mode 100644
index ec4f115c25..0000000000
--- a/t/t4018/cpp-class-constructor
+++ /dev/null
@@ -1,4 +0,0 @@
-Item::Item(int RIGHT)
-{
-	ChangeMe;
-}
diff --git a/t/t4018/cpp-class-constructor-mem-init b/t/t4018/cpp-class-constructor-mem-init
deleted file mode 100644
index 49a69f37e1..0000000000
--- a/t/t4018/cpp-class-constructor-mem-init
+++ /dev/null
@@ -1,5 +0,0 @@
-Item::Item(int RIGHT) :
-	member(0)
-{
-	ChangeMe;
-}
diff --git a/t/t4018/cpp-class-constructor-mem-init.ctx b/t/t4018/cpp-class-constructor-mem-init.ctx
deleted file mode 100644
index 6664b8b3d8..0000000000
--- a/t/t4018/cpp-class-constructor-mem-init.ctx
+++ /dev/null
@@ -1 +0,0 @@
-Item::Item(int RIGHT) :
diff --git a/t/t4018/cpp-class-constructor.ctx b/t/t4018/cpp-class-constructor.ctx
deleted file mode 100644
index 2dcadfc0ba..0000000000
--- a/t/t4018/cpp-class-constructor.ctx
+++ /dev/null
@@ -1 +0,0 @@
-Item::Item(int RIGHT)
diff --git a/t/t4018/cpp-class-definition b/t/t4018/cpp-class-definition
deleted file mode 100644
index 11b61da3b7..0000000000
--- a/t/t4018/cpp-class-definition
+++ /dev/null
@@ -1,4 +0,0 @@
-class RIGHT
-{
-	int ChangeMe;
-};
diff --git a/t/t4018/cpp-class-definition-derived b/t/t4018/cpp-class-definition-derived
deleted file mode 100644
index 3b98cd09ab..0000000000
--- a/t/t4018/cpp-class-definition-derived
+++ /dev/null
@@ -1,5 +0,0 @@
-class RIGHT :
-	public Baseclass
-{
-	int ChangeMe;
-};
diff --git a/t/t4018/cpp-class-definition-derived.ctx b/t/t4018/cpp-class-definition-derived.ctx
deleted file mode 100644
index 146f0a7b7c..0000000000
--- a/t/t4018/cpp-class-definition-derived.ctx
+++ /dev/null
@@ -1 +0,0 @@
-class RIGHT :
diff --git a/t/t4018/cpp-class-definition.ctx b/t/t4018/cpp-class-definition.ctx
deleted file mode 100644
index 54bff816d6..0000000000
--- a/t/t4018/cpp-class-definition.ctx
+++ /dev/null
@@ -1 +0,0 @@
-class RIGHT
diff --git a/t/t4018/cpp-class-destructor b/t/t4018/cpp-class-destructor
deleted file mode 100644
index 5487665096..0000000000
--- a/t/t4018/cpp-class-destructor
+++ /dev/null
@@ -1,4 +0,0 @@
-RIGHT::~RIGHT()
-{
-	ChangeMe;
-}
diff --git a/t/t4018/cpp-class-destructor.ctx b/t/t4018/cpp-class-destructor.ctx
deleted file mode 100644
index 5390c17cf6..0000000000
--- a/t/t4018/cpp-class-destructor.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT::~RIGHT()
diff --git a/t/t4018/cpp-function-returning-global-type b/t/t4018/cpp-function-returning-global-type
deleted file mode 100644
index 1084d5990e..0000000000
--- a/t/t4018/cpp-function-returning-global-type
+++ /dev/null
@@ -1,4 +0,0 @@
-::Item get::it::RIGHT()
-{
-	ChangeMe;
-}
diff --git a/t/t4018/cpp-function-returning-global-type.ctx b/t/t4018/cpp-function-returning-global-type.ctx
deleted file mode 100644
index 4dcdde25f4..0000000000
--- a/t/t4018/cpp-function-returning-global-type.ctx
+++ /dev/null
@@ -1 +0,0 @@
-::Item get::it::RIGHT()
diff --git a/t/t4018/cpp-function-returning-nested b/t/t4018/cpp-function-returning-nested
deleted file mode 100644
index d9750aa61a..0000000000
--- a/t/t4018/cpp-function-returning-nested
+++ /dev/null
@@ -1,5 +0,0 @@
-get::Item get::it::RIGHT()
-{
-	ChangeMe;
-}
-
diff --git a/t/t4018/cpp-function-returning-nested.ctx b/t/t4018/cpp-function-returning-nested.ctx
deleted file mode 100644
index 6ef73c8368..0000000000
--- a/t/t4018/cpp-function-returning-nested.ctx
+++ /dev/null
@@ -1 +0,0 @@
-get::Item get::it::RIGHT()
diff --git a/t/t4018/cpp-function-returning-pointer b/t/t4018/cpp-function-returning-pointer
deleted file mode 100644
index ef15657ea8..0000000000
--- a/t/t4018/cpp-function-returning-pointer
+++ /dev/null
@@ -1,4 +0,0 @@
-const char *get_it_RIGHT(char *ptr)
-{
-	ChangeMe;
-}
diff --git a/t/t4018/cpp-function-returning-pointer.ctx b/t/t4018/cpp-function-returning-pointer.ctx
deleted file mode 100644
index bb0acce5c7..0000000000
--- a/t/t4018/cpp-function-returning-pointer.ctx
+++ /dev/null
@@ -1 +0,0 @@
-const char *get_it_RIGHT(char *ptr)
diff --git a/t/t4018/cpp-function-returning-reference b/t/t4018/cpp-function-returning-reference
deleted file mode 100644
index 01b051df70..0000000000
--- a/t/t4018/cpp-function-returning-reference
+++ /dev/null
@@ -1,4 +0,0 @@
-string& get::it::RIGHT(char *ptr)
-{
-	ChangeMe;
-}
diff --git a/t/t4018/cpp-function-returning-reference.ctx b/t/t4018/cpp-function-returning-reference.ctx
deleted file mode 100644
index 76afe381fd..0000000000
--- a/t/t4018/cpp-function-returning-reference.ctx
+++ /dev/null
@@ -1 +0,0 @@
-string& get::it::RIGHT(char *ptr)
diff --git a/t/t4018/cpp-gnu-style-function b/t/t4018/cpp-gnu-style-function
deleted file mode 100644
index 08c7c7565a..0000000000
--- a/t/t4018/cpp-gnu-style-function
+++ /dev/null
@@ -1,5 +0,0 @@
-const char *
-RIGHT(int arg)
-{
-	ChangeMe;
-}
diff --git a/t/t4018/cpp-gnu-style-function.ctx b/t/t4018/cpp-gnu-style-function.ctx
deleted file mode 100644
index 1858287812..0000000000
--- a/t/t4018/cpp-gnu-style-function.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT(int arg)
diff --git a/t/t4018/cpp-namespace-definition b/t/t4018/cpp-namespace-definition
deleted file mode 100644
index 6749980241..0000000000
--- a/t/t4018/cpp-namespace-definition
+++ /dev/null
@@ -1,4 +0,0 @@
-namespace RIGHT
-{
-	ChangeMe;
-}
diff --git a/t/t4018/cpp-namespace-definition.ctx b/t/t4018/cpp-namespace-definition.ctx
deleted file mode 100644
index 14c29c4638..0000000000
--- a/t/t4018/cpp-namespace-definition.ctx
+++ /dev/null
@@ -1 +0,0 @@
-namespace RIGHT
diff --git a/t/t4018/cpp-operator-definition b/t/t4018/cpp-operator-definition
deleted file mode 100644
index 1acd827159..0000000000
--- a/t/t4018/cpp-operator-definition
+++ /dev/null
@@ -1,4 +0,0 @@
-Value operator+(Value LEFT, Value RIGHT)
-{
-	ChangeMe;
-}
diff --git a/t/t4018/cpp-operator-definition.ctx b/t/t4018/cpp-operator-definition.ctx
deleted file mode 100644
index 5b56778961..0000000000
--- a/t/t4018/cpp-operator-definition.ctx
+++ /dev/null
@@ -1 +0,0 @@
-Value operator+(Value LEFT, Value RIGHT)
diff --git a/t/t4018/cpp-skip-access-specifiers b/t/t4018/cpp-skip-access-specifiers
deleted file mode 100644
index 4d4a9dbb9d..0000000000
--- a/t/t4018/cpp-skip-access-specifiers
+++ /dev/null
@@ -1,8 +0,0 @@
-class RIGHT : public Baseclass
-{
-public:
-protected:
-private:
-	void DoSomething();
-	int ChangeMe;
-};
diff --git a/t/t4018/cpp-skip-access-specifiers.ctx b/t/t4018/cpp-skip-access-specifiers.ctx
deleted file mode 100644
index 075bcd883b..0000000000
--- a/t/t4018/cpp-skip-access-specifiers.ctx
+++ /dev/null
@@ -1 +0,0 @@
-class RIGHT : public Baseclass
diff --git a/t/t4018/cpp-skip-comment-block b/t/t4018/cpp-skip-comment-block
deleted file mode 100644
index 3800b9967a..0000000000
--- a/t/t4018/cpp-skip-comment-block
+++ /dev/null
@@ -1,9 +0,0 @@
-struct item RIGHT(int i)
-// Do not
-// pick up
-/* these
-** comments.
-*/
-{
-	ChangeMe;
-}
diff --git a/t/t4018/cpp-skip-comment-block.ctx b/t/t4018/cpp-skip-comment-block.ctx
deleted file mode 100644
index 656c59893d..0000000000
--- a/t/t4018/cpp-skip-comment-block.ctx
+++ /dev/null
@@ -1 +0,0 @@
-struct item RIGHT(int i)
diff --git a/t/t4018/cpp-skip-labels b/t/t4018/cpp-skip-labels
deleted file mode 100644
index b9c10aba22..0000000000
--- a/t/t4018/cpp-skip-labels
+++ /dev/null
@@ -1,8 +0,0 @@
-void RIGHT (void)
-{
-repeat:		// C++ comment
-next:		/* C comment */
-	do_something();
-
-	ChangeMe;
-}
diff --git a/t/t4018/cpp-skip-labels.ctx b/t/t4018/cpp-skip-labels.ctx
deleted file mode 100644
index 6b0635f7f7..0000000000
--- a/t/t4018/cpp-skip-labels.ctx
+++ /dev/null
@@ -1 +0,0 @@
-void RIGHT (void)
diff --git a/t/t4018/cpp-struct-definition b/t/t4018/cpp-struct-definition
deleted file mode 100644
index 521c59fd15..0000000000
--- a/t/t4018/cpp-struct-definition
+++ /dev/null
@@ -1,9 +0,0 @@
-struct RIGHT {
-	unsigned
-	/* this bit field looks like a label and should not be picked up */
-		decoy_bitfield: 2,
-		more : 1;
-	int filler;
-
-	int ChangeMe;
-};
diff --git a/t/t4018/cpp-struct-definition.ctx b/t/t4018/cpp-struct-definition.ctx
deleted file mode 100644
index 48ed893279..0000000000
--- a/t/t4018/cpp-struct-definition.ctx
+++ /dev/null
@@ -1 +0,0 @@
-struct RIGHT {
diff --git a/t/t4018/cpp-struct-single-line b/t/t4018/cpp-struct-single-line
deleted file mode 100644
index a0de5fb800..0000000000
--- a/t/t4018/cpp-struct-single-line
+++ /dev/null
@@ -1,7 +0,0 @@
-void wrong()
-{
-}
-
-struct RIGHT_iterator_tag {};
-
-int ChangeMe;
diff --git a/t/t4018/cpp-struct-single-line.ctx b/t/t4018/cpp-struct-single-line.ctx
deleted file mode 100644
index e3bc9d5017..0000000000
--- a/t/t4018/cpp-struct-single-line.ctx
+++ /dev/null
@@ -1 +0,0 @@
-struct RIGHT_iterator_tag {};
diff --git a/t/t4018/cpp-template-function-definition b/t/t4018/cpp-template-function-definition
deleted file mode 100644
index 0cdf5ba5bd..0000000000
--- a/t/t4018/cpp-template-function-definition
+++ /dev/null
@@ -1,4 +0,0 @@
-template<class T> int RIGHT(T arg)
-{
-	ChangeMe;
-}
diff --git a/t/t4018/cpp-template-function-definition.ctx b/t/t4018/cpp-template-function-definition.ctx
deleted file mode 100644
index c9da39cf65..0000000000
--- a/t/t4018/cpp-template-function-definition.ctx
+++ /dev/null
@@ -1 +0,0 @@
-template<class T> int RIGHT(T arg)
diff --git a/t/t4018/cpp-union-definition b/t/t4018/cpp-union-definition
deleted file mode 100644
index 7ec94df697..0000000000
--- a/t/t4018/cpp-union-definition
+++ /dev/null
@@ -1,4 +0,0 @@
-union RIGHT {
-	double v;
-	int ChangeMe;
-};
diff --git a/t/t4018/cpp-union-definition.ctx b/t/t4018/cpp-union-definition.ctx
deleted file mode 100644
index 2fc7b54fb8..0000000000
--- a/t/t4018/cpp-union-definition.ctx
+++ /dev/null
@@ -1 +0,0 @@
-union RIGHT {
diff --git a/t/t4018/cpp-void-c-function b/t/t4018/cpp-void-c-function
deleted file mode 100644
index 153081e872..0000000000
--- a/t/t4018/cpp-void-c-function
+++ /dev/null
@@ -1,4 +0,0 @@
-void RIGHT (void)
-{
-	ChangeMe;
-}
diff --git a/t/t4018/cpp-void-c-function.ctx b/t/t4018/cpp-void-c-function.ctx
deleted file mode 100644
index 6b0635f7f7..0000000000
--- a/t/t4018/cpp-void-c-function.ctx
+++ /dev/null
@@ -1 +0,0 @@
-void RIGHT (void)
diff --git a/t/t4018/cpp.sh b/t/t4018/cpp.sh
new file mode 100755
index 0000000000..185d40d5ef
--- /dev/null
+++ b/t/t4018/cpp.sh
@@ -0,0 +1,239 @@
+#!/bin/sh
+#
+# See ../t4018-diff-funcname.sh's test_diff_funcname()
+#
+
+test_diff_funcname 'cpp: c++ function' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+Item RIGHT::DoSomething( Args with_spaces )
+EOF_HUNK
+Item RIGHT::DoSomething( Args with_spaces )
+{
+	ChangeMe;
+}
+EOF_TEST
+
+test_diff_funcname 'cpp: class constructor' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+Item::Item(int RIGHT)
+EOF_HUNK
+Item::Item(int RIGHT)
+{
+	ChangeMe;
+}
+EOF_TEST
+
+test_diff_funcname 'cpp: class constructor mem init' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+Item::Item(int RIGHT) :
+EOF_HUNK
+Item::Item(int RIGHT) :
+	member(0)
+{
+	ChangeMe;
+}
+EOF_TEST
+
+test_diff_funcname 'cpp: class definition' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+class RIGHT
+EOF_HUNK
+class RIGHT
+{
+	int ChangeMe;
+};
+EOF_TEST
+
+test_diff_funcname 'cpp: class definition derived' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+class RIGHT :
+EOF_HUNK
+class RIGHT :
+	public Baseclass
+{
+	int ChangeMe;
+};
+EOF_TEST
+
+test_diff_funcname 'cpp: class destructor' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT::~RIGHT()
+EOF_HUNK
+RIGHT::~RIGHT()
+{
+	ChangeMe;
+}
+EOF_TEST
+
+test_diff_funcname 'cpp: function returning global type' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+::Item get::it::RIGHT()
+EOF_HUNK
+::Item get::it::RIGHT()
+{
+	ChangeMe;
+}
+EOF_TEST
+
+test_diff_funcname 'cpp: function returning nested' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+get::Item get::it::RIGHT()
+EOF_HUNK
+get::Item get::it::RIGHT()
+{
+	ChangeMe;
+}
+
+EOF_TEST
+
+test_diff_funcname 'cpp: function returning pointer' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+const char *get_it_RIGHT(char *ptr)
+EOF_HUNK
+const char *get_it_RIGHT(char *ptr)
+{
+	ChangeMe;
+}
+EOF_TEST
+
+test_diff_funcname 'cpp: function returning reference' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+string& get::it::RIGHT(char *ptr)
+EOF_HUNK
+string& get::it::RIGHT(char *ptr)
+{
+	ChangeMe;
+}
+EOF_TEST
+
+test_diff_funcname 'cpp: gnu style function' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT(int arg)
+EOF_HUNK
+const char *
+RIGHT(int arg)
+{
+	ChangeMe;
+}
+EOF_TEST
+
+test_diff_funcname 'cpp: namespace definition' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+namespace RIGHT
+EOF_HUNK
+namespace RIGHT
+{
+	ChangeMe;
+}
+EOF_TEST
+
+test_diff_funcname 'cpp: operator definition' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+Value operator+(Value LEFT, Value RIGHT)
+EOF_HUNK
+Value operator+(Value LEFT, Value RIGHT)
+{
+	ChangeMe;
+}
+EOF_TEST
+
+test_diff_funcname 'cpp: skip access specifiers' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+class RIGHT : public Baseclass
+EOF_HUNK
+class RIGHT : public Baseclass
+{
+public:
+protected:
+private:
+	void DoSomething();
+	int ChangeMe;
+};
+EOF_TEST
+
+test_diff_funcname 'cpp: skip comment block' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+struct item RIGHT(int i)
+EOF_HUNK
+struct item RIGHT(int i)
+// Do not
+// pick up
+/* these
+** comments.
+*/
+{
+	ChangeMe;
+}
+EOF_TEST
+
+test_diff_funcname 'cpp: skip labels' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+void RIGHT (void)
+EOF_HUNK
+void RIGHT (void)
+{
+repeat:		// C++ comment
+next:		/* C comment */
+	do_something();
+
+	ChangeMe;
+}
+EOF_TEST
+
+test_diff_funcname 'cpp: struct definition' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+struct RIGHT {
+EOF_HUNK
+struct RIGHT {
+	unsigned
+	/* this bit field looks like a label and should not be picked up */
+		decoy_bitfield: 2,
+		more : 1;
+	int filler;
+
+	int ChangeMe;
+};
+EOF_TEST
+
+test_diff_funcname 'cpp: struct single line' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+struct RIGHT_iterator_tag {};
+EOF_HUNK
+void wrong()
+{
+}
+
+struct RIGHT_iterator_tag {};
+
+int ChangeMe;
+EOF_TEST
+
+test_diff_funcname 'cpp: template function definition' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+template<class T> int RIGHT(T arg)
+EOF_HUNK
+template<class T> int RIGHT(T arg)
+{
+	ChangeMe;
+}
+EOF_TEST
+
+test_diff_funcname 'cpp: union definition' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+union RIGHT {
+EOF_HUNK
+union RIGHT {
+	double v;
+	int ChangeMe;
+};
+EOF_TEST
+
+test_diff_funcname 'cpp: void c function' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+void RIGHT (void)
+EOF_HUNK
+void RIGHT (void)
+{
+	ChangeMe;
+}
+EOF_TEST
diff --git a/t/t4018/css-attribute-value-selector b/t/t4018/css-attribute-value-selector
deleted file mode 100644
index 918256b20c..0000000000
--- a/t/t4018/css-attribute-value-selector
+++ /dev/null
@@ -1,4 +0,0 @@
-[class*="RIGHT"] {
-    background : #000;
-    border : 10px ChangeMe #C6C6C6;
-}
diff --git a/t/t4018/css-attribute-value-selector.ctx b/t/t4018/css-attribute-value-selector.ctx
deleted file mode 100644
index 7f8956251c..0000000000
--- a/t/t4018/css-attribute-value-selector.ctx
+++ /dev/null
@@ -1 +0,0 @@
-[class*="RIGHT"] {
diff --git a/t/t4018/css-block-level-@-statements b/t/t4018/css-block-level-@-statements
deleted file mode 100644
index d6755f2f3d..0000000000
--- a/t/t4018/css-block-level-@-statements
+++ /dev/null
@@ -1,10 +0,0 @@
-@keyframes RIGHT {
-    from {
-        background : #000;
-        border : 10px ChangeMe #C6C6C6;
-    }
-    to {
-        background : #fff;
-        border : 10px solid #C6C6C6;
-    }
-}
diff --git a/t/t4018/css-block-level-@-statements.ctx b/t/t4018/css-block-level-@-statements.ctx
deleted file mode 100644
index 7f5e90468c..0000000000
--- a/t/t4018/css-block-level-@-statements.ctx
+++ /dev/null
@@ -1 +0,0 @@
-@keyframes RIGHT {
diff --git a/t/t4018/css-brace-in-col-1 b/t/t4018/css-brace-in-col-1
deleted file mode 100644
index 7831577506..0000000000
--- a/t/t4018/css-brace-in-col-1
+++ /dev/null
@@ -1,5 +0,0 @@
-RIGHT label.control-label
-{
-    margin-top: 10px!important;
-    border : 10px ChangeMe #C6C6C6;
-}
diff --git a/t/t4018/css-brace-in-col-1.ctx b/t/t4018/css-brace-in-col-1.ctx
deleted file mode 100644
index 91a9105c6a..0000000000
--- a/t/t4018/css-brace-in-col-1.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT label.control-label
diff --git a/t/t4018/css-class-selector b/t/t4018/css-class-selector
deleted file mode 100644
index f790a0062f..0000000000
--- a/t/t4018/css-class-selector
+++ /dev/null
@@ -1,4 +0,0 @@
-.RIGHT {
-    background : #000;
-    border : 10px ChangeMe #C6C6C6;
-}
diff --git a/t/t4018/css-class-selector.ctx b/t/t4018/css-class-selector.ctx
deleted file mode 100644
index ac7367d7f4..0000000000
--- a/t/t4018/css-class-selector.ctx
+++ /dev/null
@@ -1 +0,0 @@
-.RIGHT {
diff --git a/t/t4018/css-colon-eol b/t/t4018/css-colon-eol
deleted file mode 100644
index 5a30553d29..0000000000
--- a/t/t4018/css-colon-eol
+++ /dev/null
@@ -1,4 +0,0 @@
-RIGHT h1 {
-color:
-ChangeMe;
-}
diff --git a/t/t4018/css-colon-eol.ctx b/t/t4018/css-colon-eol.ctx
deleted file mode 100644
index b68493b9b0..0000000000
--- a/t/t4018/css-colon-eol.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT h1 {
diff --git a/t/t4018/css-colon-selector b/t/t4018/css-colon-selector
deleted file mode 100644
index c6d71fb42d..0000000000
--- a/t/t4018/css-colon-selector
+++ /dev/null
@@ -1,5 +0,0 @@
-RIGHT a:hover {
-    margin-top:
-    10px!important;
-    border : 10px ChangeMe #C6C6C6;
-}
diff --git a/t/t4018/css-colon-selector.ctx b/t/t4018/css-colon-selector.ctx
deleted file mode 100644
index 00b1a5aefe..0000000000
--- a/t/t4018/css-colon-selector.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT a:hover {
diff --git a/t/t4018/css-common b/t/t4018/css-common
deleted file mode 100644
index 84ed754b33..0000000000
--- a/t/t4018/css-common
+++ /dev/null
@@ -1,4 +0,0 @@
-RIGHT label.control-label {
-    margin-top: 10px!important;
-    border : 10px ChangeMe #C6C6C6;
-}
diff --git a/t/t4018/css-common.ctx b/t/t4018/css-common.ctx
deleted file mode 100644
index 43686b4081..0000000000
--- a/t/t4018/css-common.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT label.control-label {
diff --git a/t/t4018/css-id-selector b/t/t4018/css-id-selector
deleted file mode 100644
index 17c5111052..0000000000
--- a/t/t4018/css-id-selector
+++ /dev/null
@@ -1,4 +0,0 @@
-#RIGHT {
-    background : #000;
-    border : 10px ChangeMe #C6C6C6;
-}
diff --git a/t/t4018/css-id-selector.ctx b/t/t4018/css-id-selector.ctx
deleted file mode 100644
index ce19f6d8dc..0000000000
--- a/t/t4018/css-id-selector.ctx
+++ /dev/null
@@ -1 +0,0 @@
-#RIGHT {
diff --git a/t/t4018/css-long-selector-list b/t/t4018/css-long-selector-list
deleted file mode 100644
index 7ccd25d9ed..0000000000
--- a/t/t4018/css-long-selector-list
+++ /dev/null
@@ -1,6 +0,0 @@
-p.header,
-label.control-label,
-div ul#RIGHT {
-    margin-top: 10px!important;
-    border : 10px ChangeMe #C6C6C6;
-}
diff --git a/t/t4018/css-long-selector-list.ctx b/t/t4018/css-long-selector-list.ctx
deleted file mode 100644
index bc8d0fb62c..0000000000
--- a/t/t4018/css-long-selector-list.ctx
+++ /dev/null
@@ -1 +0,0 @@
-div ul#RIGHT {
diff --git a/t/t4018/css-prop-sans-indent b/t/t4018/css-prop-sans-indent
deleted file mode 100644
index a9e3c86b3c..0000000000
--- a/t/t4018/css-prop-sans-indent
+++ /dev/null
@@ -1,5 +0,0 @@
-RIGHT, label.control-label {
-margin-top: 10px!important;
-padding: 0;
-border : 10px ChangeMe #C6C6C6;
-}
diff --git a/t/t4018/css-prop-sans-indent.ctx b/t/t4018/css-prop-sans-indent.ctx
deleted file mode 100644
index cc880b2f44..0000000000
--- a/t/t4018/css-prop-sans-indent.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT, label.control-label {
diff --git a/t/t4018/css-root-selector b/t/t4018/css-root-selector
deleted file mode 100644
index 22b958e369..0000000000
--- a/t/t4018/css-root-selector
+++ /dev/null
@@ -1,4 +0,0 @@
-:RIGHT {
-    background : #000;
-    border : 10px ChangeMe #C6C6C6;
-}
diff --git a/t/t4018/css-root-selector.ctx b/t/t4018/css-root-selector.ctx
deleted file mode 100644
index 3010cded2a..0000000000
--- a/t/t4018/css-root-selector.ctx
+++ /dev/null
@@ -1 +0,0 @@
-:RIGHT {
diff --git a/t/t4018/css-short-selector-list b/t/t4018/css-short-selector-list
deleted file mode 100644
index 6a0bdee336..0000000000
--- a/t/t4018/css-short-selector-list
+++ /dev/null
@@ -1,4 +0,0 @@
-label.control, div ul#RIGHT {
-    margin-top: 10px!important;
-    border : 10px ChangeMe #C6C6C6;
-}
diff --git a/t/t4018/css-short-selector-list.ctx b/t/t4018/css-short-selector-list.ctx
deleted file mode 100644
index 9e5d87d126..0000000000
--- a/t/t4018/css-short-selector-list.ctx
+++ /dev/null
@@ -1 +0,0 @@
-label.control, div ul#RIGHT {
diff --git a/t/t4018/css-trailing-space b/t/t4018/css-trailing-space
deleted file mode 100644
index 32b5606c70..0000000000
--- a/t/t4018/css-trailing-space
+++ /dev/null
@@ -1,5 +0,0 @@
-RIGHT label.control-label {
-    margin:10px;   
-    padding:10px;
-    border : 10px ChangeMe #C6C6C6;
-}
diff --git a/t/t4018/css-trailing-space.ctx b/t/t4018/css-trailing-space.ctx
deleted file mode 100644
index 43686b4081..0000000000
--- a/t/t4018/css-trailing-space.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT label.control-label {
diff --git a/t/t4018/css.sh b/t/t4018/css.sh
new file mode 100755
index 0000000000..106a3de242
--- /dev/null
+++ b/t/t4018/css.sh
@@ -0,0 +1,146 @@
+#!/bin/sh
+#
+# See ../t4018-diff-funcname.sh's test_diff_funcname()
+#
+
+test_diff_funcname 'css: attribute value selector' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+[class*="RIGHT"] {
+EOF_HUNK
+[class*="RIGHT"] {
+    background : #000;
+    border : 10px ChangeMe #C6C6C6;
+}
+EOF_TEST
+
+test_diff_funcname 'css: block level @ statements' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+@keyframes RIGHT {
+EOF_HUNK
+@keyframes RIGHT {
+    from {
+        background : #000;
+        border : 10px ChangeMe #C6C6C6;
+    }
+    to {
+        background : #fff;
+        border : 10px solid #C6C6C6;
+    }
+}
+EOF_TEST
+
+test_diff_funcname 'css: brace in col 1' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT label.control-label
+EOF_HUNK
+RIGHT label.control-label
+{
+    margin-top: 10px!important;
+    border : 10px ChangeMe #C6C6C6;
+}
+EOF_TEST
+
+test_diff_funcname 'css: class selector' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+.RIGHT {
+EOF_HUNK
+.RIGHT {
+    background : #000;
+    border : 10px ChangeMe #C6C6C6;
+}
+EOF_TEST
+
+test_diff_funcname 'css: colon eol' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT h1 {
+EOF_HUNK
+RIGHT h1 {
+color:
+ChangeMe;
+}
+EOF_TEST
+
+test_diff_funcname 'css: colon selector' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT a:hover {
+EOF_HUNK
+RIGHT a:hover {
+    margin-top:
+    10px!important;
+    border : 10px ChangeMe #C6C6C6;
+}
+EOF_TEST
+
+test_diff_funcname 'css: common' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT label.control-label {
+EOF_HUNK
+RIGHT label.control-label {
+    margin-top: 10px!important;
+    border : 10px ChangeMe #C6C6C6;
+}
+EOF_TEST
+
+test_diff_funcname 'css: id selector' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+#RIGHT {
+EOF_HUNK
+#RIGHT {
+    background : #000;
+    border : 10px ChangeMe #C6C6C6;
+}
+EOF_TEST
+
+test_diff_funcname 'css: long selector list' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+div ul#RIGHT {
+EOF_HUNK
+p.header,
+label.control-label,
+div ul#RIGHT {
+    margin-top: 10px!important;
+    border : 10px ChangeMe #C6C6C6;
+}
+EOF_TEST
+
+test_diff_funcname 'css: prop sans indent' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT, label.control-label {
+EOF_HUNK
+RIGHT, label.control-label {
+margin-top: 10px!important;
+padding: 0;
+border : 10px ChangeMe #C6C6C6;
+}
+EOF_TEST
+
+test_diff_funcname 'css: root selector' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+:RIGHT {
+EOF_HUNK
+:RIGHT {
+    background : #000;
+    border : 10px ChangeMe #C6C6C6;
+}
+EOF_TEST
+
+test_diff_funcname 'css: short selector list' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+label.control, div ul#RIGHT {
+EOF_HUNK
+label.control, div ul#RIGHT {
+    margin-top: 10px!important;
+    border : 10px ChangeMe #C6C6C6;
+}
+EOF_TEST
+
+test_diff_funcname 'css: trailing space' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT label.control-label {
+EOF_HUNK
+RIGHT label.control-label {
+    margin:10px;   
+    padding:10px;
+    border : 10px ChangeMe #C6C6C6;
+}
+EOF_TEST
diff --git a/t/t4018/custom1-pattern.ctx b/t/t4018/custom1-pattern.ctx
deleted file mode 100644
index d1609cc9a6..0000000000
--- a/t/t4018/custom1-pattern.ctx
+++ /dev/null
@@ -1 +0,0 @@
-int special, RIGHT;
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 e8fd59f884..f8bbccadb4
--- a/t/t4018/custom1-pattern
+++ b/t/t4018/custom1.sh
@@ -1,3 +1,12 @@
+#!/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;
@@ -15,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 f88ac318b7..0000000000
--- a/t/t4018/custom2-match-to-end-of-line
+++ /dev/null
@@ -1,8 +0,0 @@
-public class RIGHT_Beer
-{
-	int special;
-	public static void main(String args[])
-	{
-		System.out.print("ChangeMe");
-	}
-}
diff --git a/t/t4018/custom2-match-to-end-of-line.ctx b/t/t4018/custom2-match-to-end-of-line.ctx
deleted file mode 100644
index 8294c6e49b..0000000000
--- a/t/t4018/custom2-match-to-end-of-line.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT_Beer
diff --git a/t/t4018/custom2.sh b/t/t4018/custom2.sh
new file mode 100755
index 0000000000..c68421f788
--- /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.ctx b/t/t4018/custom3-alternation-in-pattern.ctx
deleted file mode 100644
index 2125474b68..0000000000
--- a/t/t4018/custom3-alternation-in-pattern.ctx
+++ /dev/null
@@ -1 +0,0 @@
-public static void main(String RIGHT[])
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 5f3769c64f..07c5c134ff
--- a/t/t4018/custom3-alternation-in-pattern
+++ b/t/t4018/custom3.sh
@@ -1,3 +1,12 @@
+#!/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;
@@ -15,3 +24,4 @@ public class Beer
 			+ "99 bottles of beer on the wall.\n");
 	}
 }
+EOF_TEST
diff --git a/t/t4018/dts-labels b/t/t4018/dts-labels
deleted file mode 100644
index b21ef8737b..0000000000
--- a/t/t4018/dts-labels
+++ /dev/null
@@ -1,9 +0,0 @@
-/ {
-	label_1: node1@ff00 {
-		label2: RIGHT {
-			vendor,some-property;
-
-			ChangeMe = <0x45-30>;
-		};
-	};
-};
diff --git a/t/t4018/dts-labels.ctx b/t/t4018/dts-labels.ctx
deleted file mode 100644
index 48d9373cab..0000000000
--- a/t/t4018/dts-labels.ctx
+++ /dev/null
@@ -1 +0,0 @@
-label2: RIGHT {
diff --git a/t/t4018/dts-node-unitless b/t/t4018/dts-node-unitless
deleted file mode 100644
index c5287d9141..0000000000
--- a/t/t4018/dts-node-unitless
+++ /dev/null
@@ -1,8 +0,0 @@
-/ {
-	label_1: node1 {
-		RIGHT {
-			prop-array = <1>, <4>;
-			ChangeMe = <0xffeedd00>;
-		};
-	};
-};
diff --git a/t/t4018/dts-node-unitless.ctx b/t/t4018/dts-node-unitless.ctx
deleted file mode 100644
index 82c8683fa1..0000000000
--- a/t/t4018/dts-node-unitless.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT {
diff --git a/t/t4018/dts-nodes b/t/t4018/dts-nodes
deleted file mode 100644
index 5a4334bb16..0000000000
--- a/t/t4018/dts-nodes
+++ /dev/null
@@ -1,8 +0,0 @@
-/ {
-	label_1: node1@ff00 {
-		RIGHT@deadf00,4000 {
-			#size-cells = <1>;
-			ChangeMe = <0xffeedd00>;
-		};
-	};
-};
diff --git a/t/t4018/dts-nodes-boolean-prop b/t/t4018/dts-nodes-boolean-prop
deleted file mode 100644
index afc6b5b404..0000000000
--- a/t/t4018/dts-nodes-boolean-prop
+++ /dev/null
@@ -1,9 +0,0 @@
-/ {
-	label_1: node1@ff00 {
-		RIGHT@deadf00,4000 {
-			boolean-prop1;
-
-			ChangeMe;
-		};
-	};
-};
diff --git a/t/t4018/dts-nodes-boolean-prop.ctx b/t/t4018/dts-nodes-boolean-prop.ctx
deleted file mode 100644
index 3a0232d55d..0000000000
--- a/t/t4018/dts-nodes-boolean-prop.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT@deadf00,4000 {
diff --git a/t/t4018/dts-nodes-comment1 b/t/t4018/dts-nodes-comment1
deleted file mode 100644
index 559dfce9b3..0000000000
--- a/t/t4018/dts-nodes-comment1
+++ /dev/null
@@ -1,8 +0,0 @@
-/ {
-	label_1: node1@ff00 {
-		RIGHT@deadf00,4000 /* &a comment */ {
-			#size-cells = <1>;
-			ChangeMe = <0xffeedd00>;
-		};
-	};
-};
diff --git a/t/t4018/dts-nodes-comment1.ctx b/t/t4018/dts-nodes-comment1.ctx
deleted file mode 100644
index ec364600b1..0000000000
--- a/t/t4018/dts-nodes-comment1.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT@deadf00,4000 /* &a comment */ {
diff --git a/t/t4018/dts-nodes-comment2 b/t/t4018/dts-nodes-comment2
deleted file mode 100644
index 27e9718b31..0000000000
--- a/t/t4018/dts-nodes-comment2
+++ /dev/null
@@ -1,8 +0,0 @@
-/ {
-	label_1: node1@ff00 {
-		RIGHT@deadf00,4000 { /* a trailing comment */ 
-			#size-cells = <1>;
-			ChangeMe = <0xffeedd00>;
-		};
-	};
-};
diff --git a/t/t4018/dts-nodes-comment2.ctx b/t/t4018/dts-nodes-comment2.ctx
deleted file mode 100644
index 75f0d75258..0000000000
--- a/t/t4018/dts-nodes-comment2.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT@deadf00,4000 { /* a trailing comment */
diff --git a/t/t4018/dts-nodes-multiline-prop b/t/t4018/dts-nodes-multiline-prop
deleted file mode 100644
index 072d58b69d..0000000000
--- a/t/t4018/dts-nodes-multiline-prop
+++ /dev/null
@@ -1,13 +0,0 @@
-/ {
-	label_1: node1@ff00 {
-		RIGHT@deadf00,4000 {
-			multilineprop = <3>,
-					<4>,
-					<5>,
-					<6>,
-					<7>;
-
-			ChangeMe = <0xffeedd00>;
-		};
-	};
-};
diff --git a/t/t4018/dts-nodes-multiline-prop.ctx b/t/t4018/dts-nodes-multiline-prop.ctx
deleted file mode 100644
index 3a0232d55d..0000000000
--- a/t/t4018/dts-nodes-multiline-prop.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT@deadf00,4000 {
diff --git a/t/t4018/dts-nodes.ctx b/t/t4018/dts-nodes.ctx
deleted file mode 100644
index 3a0232d55d..0000000000
--- a/t/t4018/dts-nodes.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT@deadf00,4000 {
diff --git a/t/t4018/dts-reference b/t/t4018/dts-reference
deleted file mode 100644
index 8f0c87d863..0000000000
--- a/t/t4018/dts-reference
+++ /dev/null
@@ -1,9 +0,0 @@
-&label_1 {
-	TEST = <455>;
-};
-
-&RIGHT {
-	vendor,some-property;
-
-	ChangeMe = <0x45-30>;
-};
diff --git a/t/t4018/dts-reference.ctx b/t/t4018/dts-reference.ctx
deleted file mode 100644
index c1e13409ee..0000000000
--- a/t/t4018/dts-reference.ctx
+++ /dev/null
@@ -1 +0,0 @@
-&RIGHT {
diff --git a/t/t4018/dts-root b/t/t4018/dts-root
deleted file mode 100644
index 4353b8220c..0000000000
--- a/t/t4018/dts-root
+++ /dev/null
@@ -1,5 +0,0 @@
-/ { RIGHT /* Technically just supposed to be a slash and brace */
-	#size-cells = <1>;
-
-	ChangeMe = <0xffeedd00>;
-};
diff --git a/t/t4018/dts-root-comment b/t/t4018/dts-root-comment
deleted file mode 100644
index 333a625c70..0000000000
--- a/t/t4018/dts-root-comment
+++ /dev/null
@@ -1,8 +0,0 @@
-/ { RIGHT /* Technically just supposed to be a slash and brace */
-	#size-cells = <1>;
-
-	/* This comment should be ignored */
-
-	some-property = <40+2>;
-	ChangeMe = <0xffeedd00>;
-};
diff --git a/t/t4018/dts-root-comment.ctx b/t/t4018/dts-root-comment.ctx
deleted file mode 100644
index 656053dd42..0000000000
--- a/t/t4018/dts-root-comment.ctx
+++ /dev/null
@@ -1 +0,0 @@
-/ { RIGHT /* Technically just supposed to be a slash and brace */
diff --git a/t/t4018/dts-root.ctx b/t/t4018/dts-root.ctx
deleted file mode 100644
index 656053dd42..0000000000
--- a/t/t4018/dts-root.ctx
+++ /dev/null
@@ -1 +0,0 @@
-/ { RIGHT /* Technically just supposed to be a slash and brace */
diff --git a/t/t4018/dts.sh b/t/t4018/dts.sh
new file mode 100755
index 0000000000..304c131d86
--- /dev/null
+++ b/t/t4018/dts.sh
@@ -0,0 +1,149 @@
+#!/bin/sh
+#
+# See ../t4018-diff-funcname.sh's test_diff_funcname()
+#
+
+test_diff_funcname 'dts: labels' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+label2: RIGHT {
+EOF_HUNK
+/ {
+	label_1: node1@ff00 {
+		label2: RIGHT {
+			vendor,some-property;
+
+			ChangeMe = <0x45-30>;
+		};
+	};
+};
+EOF_TEST
+
+test_diff_funcname 'dts: node unitless' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT {
+EOF_HUNK
+/ {
+	label_1: node1 {
+		RIGHT {
+			prop-array = <1>, <4>;
+			ChangeMe = <0xffeedd00>;
+		};
+	};
+};
+EOF_TEST
+
+test_diff_funcname 'dts: nodes' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT@deadf00,4000 {
+EOF_HUNK
+/ {
+	label_1: node1@ff00 {
+		RIGHT@deadf00,4000 {
+			#size-cells = <1>;
+			ChangeMe = <0xffeedd00>;
+		};
+	};
+};
+EOF_TEST
+
+test_diff_funcname 'dts: nodes boolean prop' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT@deadf00,4000 {
+EOF_HUNK
+/ {
+	label_1: node1@ff00 {
+		RIGHT@deadf00,4000 {
+			boolean-prop1;
+
+			ChangeMe;
+		};
+	};
+};
+EOF_TEST
+
+test_diff_funcname 'dts: nodes comment1' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT@deadf00,4000 /* &a comment */ {
+EOF_HUNK
+/ {
+	label_1: node1@ff00 {
+		RIGHT@deadf00,4000 /* &a comment */ {
+			#size-cells = <1>;
+			ChangeMe = <0xffeedd00>;
+		};
+	};
+};
+EOF_TEST
+
+test_diff_funcname 'dts: nodes comment2' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT@deadf00,4000 { /* a trailing comment */
+EOF_HUNK
+/ {
+	label_1: node1@ff00 {
+		RIGHT@deadf00,4000 { /* a trailing comment */ 
+			#size-cells = <1>;
+			ChangeMe = <0xffeedd00>;
+		};
+	};
+};
+EOF_TEST
+
+test_diff_funcname 'dts: nodes multiline prop' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT@deadf00,4000 {
+EOF_HUNK
+/ {
+	label_1: node1@ff00 {
+		RIGHT@deadf00,4000 {
+			multilineprop = <3>,
+					<4>,
+					<5>,
+					<6>,
+					<7>;
+
+			ChangeMe = <0xffeedd00>;
+		};
+	};
+};
+EOF_TEST
+
+test_diff_funcname 'dts: reference' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+&RIGHT {
+EOF_HUNK
+&label_1 {
+	TEST = <455>;
+};
+
+&RIGHT {
+	vendor,some-property;
+
+	ChangeMe = <0x45-30>;
+};
+EOF_TEST
+
+test_diff_funcname 'dts: root' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+/ { RIGHT /* Technically just supposed to be a slash and brace */
+EOF_HUNK
+/ { RIGHT /* Technically just supposed to be a slash and brace */
+	#size-cells = <1>;
+
+	ChangeMe = <0xffeedd00>;
+};
+EOF_TEST
+
+test_diff_funcname 'dts: root comment' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+/ { RIGHT /* Technically just supposed to be a slash and brace */
+EOF_HUNK
+/ { RIGHT /* Technically just supposed to be a slash and brace */
+	#size-cells = <1>;
+
+	/* This comment should be ignored */
+
+	some-property = <40+2>;
+	ChangeMe = <0xffeedd00>;
+};
+EOF_TEST
diff --git a/t/t4018/elixir-do-not-pick-end b/t/t4018/elixir-do-not-pick-end
deleted file mode 100644
index fae08ba7e8..0000000000
--- a/t/t4018/elixir-do-not-pick-end
+++ /dev/null
@@ -1,5 +0,0 @@
-defmodule RIGHT do
-end
-#
-#
-# ChangeMe; do not pick up 'end' line
diff --git a/t/t4018/elixir-do-not-pick-end.ctx b/t/t4018/elixir-do-not-pick-end.ctx
deleted file mode 100644
index 8f28a7a689..0000000000
--- a/t/t4018/elixir-do-not-pick-end.ctx
+++ /dev/null
@@ -1 +0,0 @@
-defmodule RIGHT do
diff --git a/t/t4018/elixir-ex-unit-test b/t/t4018/elixir-ex-unit-test
deleted file mode 100644
index 0560a2b697..0000000000
--- a/t/t4018/elixir-ex-unit-test
+++ /dev/null
@@ -1,6 +0,0 @@
-defmodule Test do
-  test "RIGHT" do
-    assert true == true
-    assert ChangeMe
-  end
-end
diff --git a/t/t4018/elixir-ex-unit-test.ctx b/t/t4018/elixir-ex-unit-test.ctx
deleted file mode 100644
index a55e3de2cc..0000000000
--- a/t/t4018/elixir-ex-unit-test.ctx
+++ /dev/null
@@ -1 +0,0 @@
-test "RIGHT" do
diff --git a/t/t4018/elixir-function b/t/t4018/elixir-function
deleted file mode 100644
index d452f495a7..0000000000
--- a/t/t4018/elixir-function
+++ /dev/null
@@ -1,5 +0,0 @@
-def function(RIGHT, arg) do
-  # comment
-  # comment
-  ChangeMe
-end
diff --git a/t/t4018/elixir-function.ctx b/t/t4018/elixir-function.ctx
deleted file mode 100644
index 62aee9c8b1..0000000000
--- a/t/t4018/elixir-function.ctx
+++ /dev/null
@@ -1 +0,0 @@
-def function(RIGHT, arg) do
diff --git a/t/t4018/elixir-macro b/t/t4018/elixir-macro
deleted file mode 100644
index 4f925e9ad4..0000000000
--- a/t/t4018/elixir-macro
+++ /dev/null
@@ -1,5 +0,0 @@
-defmacro foo(RIGHT) do
-  # Code
-  # Code
-  ChangeMe
-end
diff --git a/t/t4018/elixir-macro.ctx b/t/t4018/elixir-macro.ctx
deleted file mode 100644
index fc1d3b85e8..0000000000
--- a/t/t4018/elixir-macro.ctx
+++ /dev/null
@@ -1 +0,0 @@
-defmacro foo(RIGHT) do
diff --git a/t/t4018/elixir-module b/t/t4018/elixir-module
deleted file mode 100644
index 91a4e7aa20..0000000000
--- a/t/t4018/elixir-module
+++ /dev/null
@@ -1,9 +0,0 @@
-defmodule RIGHT do
-  @moduledoc """
-  Foo bar
-  """
-
-  def ChangeMe(a) where is_map(a) do
-    a
-  end
-end
diff --git a/t/t4018/elixir-module-func b/t/t4018/elixir-module-func
deleted file mode 100644
index c9910d0675..0000000000
--- a/t/t4018/elixir-module-func
+++ /dev/null
@@ -1,8 +0,0 @@
-defmodule Foo do
-  def fun(RIGHT) do
-     # Code
-     # Code
-     # Code
-     ChangeMe
-  end
-end
diff --git a/t/t4018/elixir-module-func.ctx b/t/t4018/elixir-module-func.ctx
deleted file mode 100644
index 8239214386..0000000000
--- a/t/t4018/elixir-module-func.ctx
+++ /dev/null
@@ -1 +0,0 @@
-def fun(RIGHT) do
diff --git a/t/t4018/elixir-module.ctx b/t/t4018/elixir-module.ctx
deleted file mode 100644
index 8f28a7a689..0000000000
--- a/t/t4018/elixir-module.ctx
+++ /dev/null
@@ -1 +0,0 @@
-defmodule RIGHT do
diff --git a/t/t4018/elixir-nested-module b/t/t4018/elixir-nested-module
deleted file mode 100644
index 771ebc5c42..0000000000
--- a/t/t4018/elixir-nested-module
+++ /dev/null
@@ -1,9 +0,0 @@
-defmodule MyApp.RIGHT do
-  @moduledoc """
-  Foo bar
-  """
-
-  def ChangeMe(a) where is_map(a) do
-    a
-  end
-end
diff --git a/t/t4018/elixir-nested-module.ctx b/t/t4018/elixir-nested-module.ctx
deleted file mode 100644
index 3ffbdd18b1..0000000000
--- a/t/t4018/elixir-nested-module.ctx
+++ /dev/null
@@ -1 +0,0 @@
-defmodule MyApp.RIGHT do
diff --git a/t/t4018/elixir-private-function b/t/t4018/elixir-private-function
deleted file mode 100644
index 1aabe33b7a..0000000000
--- a/t/t4018/elixir-private-function
+++ /dev/null
@@ -1,5 +0,0 @@
-defp function(RIGHT, arg) do
-  # comment
-  # comment
-  ChangeMe
-end
diff --git a/t/t4018/elixir-private-function.ctx b/t/t4018/elixir-private-function.ctx
deleted file mode 100644
index 1c4eba44f7..0000000000
--- a/t/t4018/elixir-private-function.ctx
+++ /dev/null
@@ -1 +0,0 @@
-defp function(RIGHT, arg) do
diff --git a/t/t4018/elixir-protocol b/t/t4018/elixir-protocol
deleted file mode 100644
index 7d9173691e..0000000000
--- a/t/t4018/elixir-protocol
+++ /dev/null
@@ -1,6 +0,0 @@
-defprotocol RIGHT do
-  @doc """
-  Calculates the size (and not the length!) of a data structure
-  """
-  def size(data, ChangeMe)
-end
diff --git a/t/t4018/elixir-protocol-implementation b/t/t4018/elixir-protocol-implementation
deleted file mode 100644
index f9234bbfc4..0000000000
--- a/t/t4018/elixir-protocol-implementation
+++ /dev/null
@@ -1,5 +0,0 @@
-defimpl RIGHT do
-  # Docs
-  # Docs
-  def foo(ChangeMe), do: :ok
-end
diff --git a/t/t4018/elixir-protocol-implementation.ctx b/t/t4018/elixir-protocol-implementation.ctx
deleted file mode 100644
index efb758aea6..0000000000
--- a/t/t4018/elixir-protocol-implementation.ctx
+++ /dev/null
@@ -1 +0,0 @@
-defimpl RIGHT do
diff --git a/t/t4018/elixir-protocol.ctx b/t/t4018/elixir-protocol.ctx
deleted file mode 100644
index d0204e9f14..0000000000
--- a/t/t4018/elixir-protocol.ctx
+++ /dev/null
@@ -1 +0,0 @@
-defprotocol RIGHT do
diff --git a/t/t4018/elixir.sh b/t/t4018/elixir.sh
new file mode 100755
index 0000000000..6b09df9520
--- /dev/null
+++ b/t/t4018/elixir.sh
@@ -0,0 +1,127 @@
+#!/bin/sh
+#
+# See ../t4018-diff-funcname.sh's test_diff_funcname()
+#
+
+test_diff_funcname 'elixir: do not pick end' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+defmodule RIGHT do
+EOF_HUNK
+defmodule RIGHT do
+end
+#
+#
+# ChangeMe; do not pick up 'end' line
+EOF_TEST
+
+test_diff_funcname 'elixir: ex unit test' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+test "RIGHT" do
+EOF_HUNK
+defmodule Test do
+  test "RIGHT" do
+    assert true == true
+    assert ChangeMe
+  end
+end
+EOF_TEST
+
+test_diff_funcname 'elixir: function' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+def function(RIGHT, arg) do
+EOF_HUNK
+def function(RIGHT, arg) do
+  # comment
+  # comment
+  ChangeMe
+end
+EOF_TEST
+
+test_diff_funcname 'elixir: macro' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+defmacro foo(RIGHT) do
+EOF_HUNK
+defmacro foo(RIGHT) do
+  # Code
+  # Code
+  ChangeMe
+end
+EOF_TEST
+
+test_diff_funcname 'elixir: module' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+defmodule RIGHT do
+EOF_HUNK
+defmodule RIGHT do
+  @moduledoc """
+  Foo bar
+  """
+
+  def ChangeMe(a) where is_map(a) do
+    a
+  end
+end
+EOF_TEST
+
+test_diff_funcname 'elixir: module func' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+def fun(RIGHT) do
+EOF_HUNK
+defmodule Foo do
+  def fun(RIGHT) do
+     # Code
+     # Code
+     # Code
+     ChangeMe
+  end
+end
+EOF_TEST
+
+test_diff_funcname 'elixir: nested module' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+defmodule MyApp.RIGHT do
+EOF_HUNK
+defmodule MyApp.RIGHT do
+  @moduledoc """
+  Foo bar
+  """
+
+  def ChangeMe(a) where is_map(a) do
+    a
+  end
+end
+EOF_TEST
+
+test_diff_funcname 'elixir: private function' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+defp function(RIGHT, arg) do
+EOF_HUNK
+defp function(RIGHT, arg) do
+  # comment
+  # comment
+  ChangeMe
+end
+EOF_TEST
+
+test_diff_funcname 'elixir: protocol' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+defprotocol RIGHT do
+EOF_HUNK
+defprotocol RIGHT do
+  @doc """
+  Calculates the size (and not the length!) of a data structure
+  """
+  def size(data, ChangeMe)
+end
+EOF_TEST
+
+test_diff_funcname 'elixir: protocol implementation' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+defimpl RIGHT do
+EOF_HUNK
+defimpl RIGHT do
+  # Docs
+  # Docs
+  def foo(ChangeMe), do: :ok
+end
+EOF_TEST
diff --git a/t/t4018/fortran-block-data b/t/t4018/fortran-block-data
deleted file mode 100644
index 63d4e21d0a..0000000000
--- a/t/t4018/fortran-block-data
+++ /dev/null
@@ -1,5 +0,0 @@
-       BLOCK DATA RIGHT
-       
-       COMMON /B/ C, ChangeMe
-       DATA C, ChangeMe  / 2.0, 6.0 / 
-       END 
diff --git a/t/t4018/fortran-block-data.ctx b/t/t4018/fortran-block-data.ctx
deleted file mode 100644
index c3db084ccc..0000000000
--- a/t/t4018/fortran-block-data.ctx
+++ /dev/null
@@ -1 +0,0 @@
-BLOCK DATA RIGHT
diff --git a/t/t4018/fortran-comment b/t/t4018/fortran-comment
deleted file mode 100644
index 7b10d17658..0000000000
--- a/t/t4018/fortran-comment
+++ /dev/null
@@ -1,13 +0,0 @@
-      module a
-
-      contains
-
-      ! subroutine wrong
-      subroutine RIGHT
-      ! subroutine wrong
-
-      real ChangeMe
-
-      end subroutine RIGHT
-
-      end module a
diff --git a/t/t4018/fortran-comment-keyword b/t/t4018/fortran-comment-keyword
deleted file mode 100644
index e9206a5379..0000000000
--- a/t/t4018/fortran-comment-keyword
+++ /dev/null
@@ -1,14 +0,0 @@
-      module a
-
-      contains
-
-      subroutine RIGHT (funcA, funcB)
-
-      real funcA  ! grid function a
-      real funcB  ! grid function b
-
-      real ChangeMe
-
-      end subroutine RIGHT
-
-      end module a
diff --git a/t/t4018/fortran-comment-keyword.ctx b/t/t4018/fortran-comment-keyword.ctx
deleted file mode 100644
index 0b9220b355..0000000000
--- a/t/t4018/fortran-comment-keyword.ctx
+++ /dev/null
@@ -1 +0,0 @@
-subroutine RIGHT (funcA, funcB)
diff --git a/t/t4018/fortran-comment-legacy b/t/t4018/fortran-comment-legacy
deleted file mode 100644
index 53cd062c1e..0000000000
--- a/t/t4018/fortran-comment-legacy
+++ /dev/null
@@ -1,13 +0,0 @@
-      module a
-
-      contains
-
-C subroutine wrong
-      subroutine RIGHT
-C subroutine wrong
-
-      real ChangeMe
-
-      end subroutine RIGHT
-
-      end module a
diff --git a/t/t4018/fortran-comment-legacy-star b/t/t4018/fortran-comment-legacy-star
deleted file mode 100644
index 2cbcdc3d8a..0000000000
--- a/t/t4018/fortran-comment-legacy-star
+++ /dev/null
@@ -1,13 +0,0 @@
-      module a
-
-      contains
-
-* subroutine wrong
-      subroutine RIGHT
-* subroutine wrong
-
-      real ChangeMe
-
-      end subroutine RIGHT
-
-      end module a
diff --git a/t/t4018/fortran-comment-legacy-star.ctx b/t/t4018/fortran-comment-legacy-star.ctx
deleted file mode 100644
index 6a34203f80..0000000000
--- a/t/t4018/fortran-comment-legacy-star.ctx
+++ /dev/null
@@ -1 +0,0 @@
-subroutine RIGHT
diff --git a/t/t4018/fortran-comment-legacy.ctx b/t/t4018/fortran-comment-legacy.ctx
deleted file mode 100644
index 6a34203f80..0000000000
--- a/t/t4018/fortran-comment-legacy.ctx
+++ /dev/null
@@ -1 +0,0 @@
-subroutine RIGHT
diff --git a/t/t4018/fortran-comment.ctx b/t/t4018/fortran-comment.ctx
deleted file mode 100644
index 6a34203f80..0000000000
--- a/t/t4018/fortran-comment.ctx
+++ /dev/null
@@ -1 +0,0 @@
-subroutine RIGHT
diff --git a/t/t4018/fortran-external-function b/t/t4018/fortran-external-function
deleted file mode 100644
index 5a2d85d3aa..0000000000
--- a/t/t4018/fortran-external-function
+++ /dev/null
@@ -1,9 +0,0 @@
-function RIGHT(a, b) result(c)
-
-integer, intent(in) :: ChangeMe
-integer, intent(in) :: b
-integer, intent(out) :: c
-
-c = a+b
-
-end function RIGHT
diff --git a/t/t4018/fortran-external-function.ctx b/t/t4018/fortran-external-function.ctx
deleted file mode 100644
index 56ec4d8eca..0000000000
--- a/t/t4018/fortran-external-function.ctx
+++ /dev/null
@@ -1 +0,0 @@
-function RIGHT(a, b) result(c)
diff --git a/t/t4018/fortran-external-subroutine b/t/t4018/fortran-external-subroutine
deleted file mode 100644
index 4ce85fea13..0000000000
--- a/t/t4018/fortran-external-subroutine
+++ /dev/null
@@ -1,5 +0,0 @@
-subroutine RIGHT
-
-real ChangeMe
-
-end subroutine RIGHT
diff --git a/t/t4018/fortran-external-subroutine.ctx b/t/t4018/fortran-external-subroutine.ctx
deleted file mode 100644
index 6a34203f80..0000000000
--- a/t/t4018/fortran-external-subroutine.ctx
+++ /dev/null
@@ -1 +0,0 @@
-subroutine RIGHT
diff --git a/t/t4018/fortran-module b/t/t4018/fortran-module
deleted file mode 100644
index c4b737dac3..0000000000
--- a/t/t4018/fortran-module
+++ /dev/null
@@ -1,5 +0,0 @@
-module RIGHT
-
-use ChangeMe
-
-end module RIGHT
diff --git a/t/t4018/fortran-module-procedure b/t/t4018/fortran-module-procedure
deleted file mode 100644
index 1ce6d854c2..0000000000
--- a/t/t4018/fortran-module-procedure
+++ /dev/null
@@ -1,13 +0,0 @@
- module RIGHT
-
-   implicit none
-   private
-
-   interface letters  ! generic interface
-      module procedure aaaa, &
-                       bbbb, &
-                       ChangeMe, &
-                       dddd
-   end interface
-   
-end module RIGHT
diff --git a/t/t4018/fortran-module-procedure.ctx b/t/t4018/fortran-module-procedure.ctx
deleted file mode 100644
index 4f5ff2e4b8..0000000000
--- a/t/t4018/fortran-module-procedure.ctx
+++ /dev/null
@@ -1 +0,0 @@
-module RIGHT
diff --git a/t/t4018/fortran-module.ctx b/t/t4018/fortran-module.ctx
deleted file mode 100644
index 4f5ff2e4b8..0000000000
--- a/t/t4018/fortran-module.ctx
+++ /dev/null
@@ -1 +0,0 @@
-module RIGHT
diff --git a/t/t4018/fortran-program b/t/t4018/fortran-program
deleted file mode 100644
index 4616895e4b..0000000000
--- a/t/t4018/fortran-program
+++ /dev/null
@@ -1,5 +0,0 @@
-program RIGHT
-
-call ChangeMe
-
-end program RIGHT
diff --git a/t/t4018/fortran-program.ctx b/t/t4018/fortran-program.ctx
deleted file mode 100644
index c4e844df30..0000000000
--- a/t/t4018/fortran-program.ctx
+++ /dev/null
@@ -1 +0,0 @@
-program RIGHT
diff --git a/t/t4018/fortran.sh b/t/t4018/fortran.sh
new file mode 100755
index 0000000000..7b0c6789d3
--- /dev/null
+++ b/t/t4018/fortran.sh
@@ -0,0 +1,159 @@
+#!/bin/sh
+#
+# See ../t4018-diff-funcname.sh's test_diff_funcname()
+#
+
+test_diff_funcname 'fortran: block data' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+BLOCK DATA RIGHT
+EOF_HUNK
+       BLOCK DATA RIGHT
+       
+       COMMON /B/ C, ChangeMe
+       DATA C, ChangeMe  / 2.0, 6.0 / 
+       END 
+EOF_TEST
+
+test_diff_funcname 'fortran: comment' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+subroutine RIGHT
+EOF_HUNK
+      module a
+
+      contains
+
+      ! subroutine wrong
+      subroutine RIGHT
+      ! subroutine wrong
+
+      real ChangeMe
+
+      end subroutine RIGHT
+
+      end module a
+EOF_TEST
+
+test_diff_funcname 'fortran: comment keyword' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+subroutine RIGHT (funcA, funcB)
+EOF_HUNK
+      module a
+
+      contains
+
+      subroutine RIGHT (funcA, funcB)
+
+      real funcA  ! grid function a
+      real funcB  ! grid function b
+
+      real ChangeMe
+
+      end subroutine RIGHT
+
+      end module a
+EOF_TEST
+
+test_diff_funcname 'fortran: comment legacy' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+subroutine RIGHT
+EOF_HUNK
+      module a
+
+      contains
+
+C subroutine wrong
+      subroutine RIGHT
+C subroutine wrong
+
+      real ChangeMe
+
+      end subroutine RIGHT
+
+      end module a
+EOF_TEST
+
+test_diff_funcname 'fortran: comment legacy star' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+subroutine RIGHT
+EOF_HUNK
+      module a
+
+      contains
+
+* subroutine wrong
+      subroutine RIGHT
+* subroutine wrong
+
+      real ChangeMe
+
+      end subroutine RIGHT
+
+      end module a
+EOF_TEST
+
+test_diff_funcname 'fortran: external function' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+function RIGHT(a, b) result(c)
+EOF_HUNK
+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_TEST
+
+test_diff_funcname 'fortran: external subroutine' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+subroutine RIGHT
+EOF_HUNK
+subroutine RIGHT
+
+real ChangeMe
+
+end subroutine RIGHT
+EOF_TEST
+
+test_diff_funcname 'fortran: module' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+module RIGHT
+EOF_HUNK
+module RIGHT
+
+use ChangeMe
+
+end module RIGHT
+EOF_TEST
+
+test_diff_funcname 'fortran: module procedure' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+module RIGHT
+EOF_HUNK
+ module RIGHT
+
+   implicit none
+   private
+
+   interface letters  ! generic interface
+      module procedure aaaa, &
+                       bbbb, &
+                       ChangeMe, &
+                       dddd
+   end interface
+   
+end module RIGHT
+EOF_TEST
+
+test_diff_funcname 'fortran: program' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+program RIGHT
+EOF_HUNK
+program RIGHT
+
+call ChangeMe
+
+end program RIGHT
+EOF_TEST
diff --git a/t/t4018/fountain-scene b/t/t4018/fountain-scene
deleted file mode 100644
index 6b3257d680..0000000000
--- a/t/t4018/fountain-scene
+++ /dev/null
@@ -1,4 +0,0 @@
-EXT. STREET RIGHT OUTSIDE - DAY
-
-CHARACTER
-You didn't say the magic phrase, "ChangeMe".
diff --git a/t/t4018/fountain-scene.ctx b/t/t4018/fountain-scene.ctx
deleted file mode 100644
index bf10171418..0000000000
--- a/t/t4018/fountain-scene.ctx
+++ /dev/null
@@ -1 +0,0 @@
-EXT. STREET RIGHT OUTSIDE - DAY
diff --git a/t/t4018/fountain.sh b/t/t4018/fountain.sh
new file mode 100755
index 0000000000..02b44d6a3f
--- /dev/null
+++ b/t/t4018/fountain.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+#
+# See ../t4018-diff-funcname.sh's test_diff_funcname()
+#
+
+test_diff_funcname 'fountain: scene' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+EXT. STREET RIGHT OUTSIDE - DAY
+EOF_HUNK
+EXT. STREET RIGHT OUTSIDE - DAY
+
+CHARACTER
+You didn't say the magic phrase, "ChangeMe".
+EOF_TEST
diff --git a/t/t4018/golang-complex-function b/t/t4018/golang-complex-function
deleted file mode 100644
index e057dcefed..0000000000
--- a/t/t4018/golang-complex-function
+++ /dev/null
@@ -1,8 +0,0 @@
-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-complex-function.ctx b/t/t4018/golang-complex-function.ctx
deleted file mode 100644
index 8e8d5582ff..0000000000
--- a/t/t4018/golang-complex-function.ctx
+++ /dev/null
@@ -1 +0,0 @@
-func (t *Test) RIGHT(a Type) (Type, error) {
diff --git a/t/t4018/golang-func b/t/t4018/golang-func
deleted file mode 100644
index 8e9c9ac7c3..0000000000
--- a/t/t4018/golang-func
+++ /dev/null
@@ -1,4 +0,0 @@
-func RIGHT() {
-	a := 5
-	b := ChangeMe
-}
diff --git a/t/t4018/golang-func.ctx b/t/t4018/golang-func.ctx
deleted file mode 100644
index 88bc823813..0000000000
--- a/t/t4018/golang-func.ctx
+++ /dev/null
@@ -1 +0,0 @@
-func RIGHT() {
diff --git a/t/t4018/golang-interface b/t/t4018/golang-interface
deleted file mode 100644
index 553bedec96..0000000000
--- a/t/t4018/golang-interface
+++ /dev/null
@@ -1,4 +0,0 @@
-type RIGHT interface {
-	a() Type
-	b() ChangeMe
-}
diff --git a/t/t4018/golang-interface.ctx b/t/t4018/golang-interface.ctx
deleted file mode 100644
index 2d07f5a383..0000000000
--- a/t/t4018/golang-interface.ctx
+++ /dev/null
@@ -1 +0,0 @@
-type RIGHT interface {
diff --git a/t/t4018/golang-long-func b/t/t4018/golang-long-func
deleted file mode 100644
index ac3a77b5c4..0000000000
--- a/t/t4018/golang-long-func
+++ /dev/null
@@ -1,5 +0,0 @@
-func RIGHT(aVeryVeryVeryLongVariableName AVeryVeryVeryLongType,
-	anotherLongVariableName AnotherLongType) {
-	a := 5
-	b := ChangeMe
-}
diff --git a/t/t4018/golang-long-func.ctx b/t/t4018/golang-long-func.ctx
deleted file mode 100644
index 25635e712e..0000000000
--- a/t/t4018/golang-long-func.ctx
+++ /dev/null
@@ -1 +0,0 @@
-func RIGHT(aVeryVeryVeryLongVariableName AVeryVeryVeryLongType,
diff --git a/t/t4018/golang-struct b/t/t4018/golang-struct
deleted file mode 100644
index 5deda77fee..0000000000
--- a/t/t4018/golang-struct
+++ /dev/null
@@ -1,4 +0,0 @@
-type RIGHT struct {
-	a Type
-	b ChangeMe
-}
diff --git a/t/t4018/golang-struct.ctx b/t/t4018/golang-struct.ctx
deleted file mode 100644
index 8a1240699d..0000000000
--- a/t/t4018/golang-struct.ctx
+++ /dev/null
@@ -1 +0,0 @@
-type RIGHT struct {
diff --git a/t/t4018/golang.sh b/t/t4018/golang.sh
new file mode 100755
index 0000000000..bf22f58c12
--- /dev/null
+++ b/t/t4018/golang.sh
@@ -0,0 +1,59 @@
+#!/bin/sh
+#
+# See ../t4018-diff-funcname.sh's test_diff_funcname()
+#
+
+test_diff_funcname 'golang: complex function' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+func (t *Test) RIGHT(a Type) (Type, error) {
+EOF_HUNK
+type Test struct {
+	a Type
+}
+
+func (t *Test) RIGHT(a Type) (Type, error) {
+	t.a = a
+	return ChangeMe, nil
+}
+EOF_TEST
+
+test_diff_funcname 'golang: func' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+func RIGHT() {
+EOF_HUNK
+func RIGHT() {
+	a := 5
+	b := ChangeMe
+}
+EOF_TEST
+
+test_diff_funcname 'golang: interface' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+type RIGHT interface {
+EOF_HUNK
+type RIGHT interface {
+	a() Type
+	b() ChangeMe
+}
+EOF_TEST
+
+test_diff_funcname 'golang: long func' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+func RIGHT(aVeryVeryVeryLongVariableName AVeryVeryVeryLongType,
+EOF_HUNK
+func RIGHT(aVeryVeryVeryLongVariableName AVeryVeryVeryLongType,
+	anotherLongVariableName AnotherLongType) {
+	a := 5
+	b := ChangeMe
+}
+EOF_TEST
+
+test_diff_funcname 'golang: struct' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+type RIGHT struct {
+EOF_HUNK
+type RIGHT struct {
+	a Type
+	b ChangeMe
+}
+EOF_TEST
diff --git a/t/t4018/java-class-member-function b/t/t4018/java-class-member-function
deleted file mode 100644
index 298bc7a71b..0000000000
--- a/t/t4018/java-class-member-function
+++ /dev/null
@@ -1,8 +0,0 @@
-public class Beer
-{
-	int special;
-	public static void main(String RIGHT[])
-	{
-		System.out.print("ChangeMe");
-	}
-}
diff --git a/t/t4018/java-class-member-function.ctx b/t/t4018/java-class-member-function.ctx
deleted file mode 100644
index 2125474b68..0000000000
--- a/t/t4018/java-class-member-function.ctx
+++ /dev/null
@@ -1 +0,0 @@
-public static void main(String RIGHT[])
diff --git a/t/t4018/java.sh b/t/t4018/java.sh
new file mode 100755
index 0000000000..c89cf2f9d8
--- /dev/null
+++ b/t/t4018/java.sh
@@ -0,0 +1,18 @@
+#!/bin/sh
+#
+# See ../t4018-diff-funcname.sh's test_diff_funcname()
+#
+
+test_diff_funcname 'java: class member function' \
+	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[])
+	{
+		System.out.print("ChangeMe");
+	}
+}
+EOF_TEST
diff --git a/t/t4018/markdown-heading-indented b/t/t4018/markdown-heading-indented
deleted file mode 100644
index 1991c2bd45..0000000000
--- a/t/t4018/markdown-heading-indented
+++ /dev/null
@@ -1,6 +0,0 @@
-Indented headings are allowed, as long as the indent is no more than 3 spaces.
-
-   ### RIGHT
-
-- something
-- ChangeMe
diff --git a/t/t4018/markdown-heading-indented.ctx b/t/t4018/markdown-heading-indented.ctx
deleted file mode 100644
index 5938336743..0000000000
--- a/t/t4018/markdown-heading-indented.ctx
+++ /dev/null
@@ -1 +0,0 @@
-   ### RIGHT
diff --git a/t/t4018/markdown-heading-non-headings b/t/t4018/markdown-heading-non-headings
deleted file mode 100644
index c479c1a3f1..0000000000
--- a/t/t4018/markdown-heading-non-headings
+++ /dev/null
@@ -1,17 +0,0 @@
-Headings can be right next to other lines of the file:
-# RIGHT
-Indents of four or more spaces make a code block:
-
-    # code comment, not heading
-
-If there's no space after the final hash, it's not a heading:
-
-#hashtag
-
-Sequences of more than 6 hashes don't make a heading:
-
-####### over-enthusiastic heading
-
-So the detected heading should be right up at the start of this file.
-
-ChangeMe
diff --git a/t/t4018/markdown-heading-non-headings.ctx b/t/t4018/markdown-heading-non-headings.ctx
deleted file mode 100644
index 7e2165be6e..0000000000
--- a/t/t4018/markdown-heading-non-headings.ctx
+++ /dev/null
@@ -1 +0,0 @@
-# RIGHT
diff --git a/t/t4018/markdown.sh b/t/t4018/markdown.sh
new file mode 100755
index 0000000000..3e1c79b139
--- /dev/null
+++ b/t/t4018/markdown.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+#
+# See ../t4018-diff-funcname.sh's test_diff_funcname()
+#
+
+test_diff_funcname 'markdown: heading indented' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+   ### RIGHT
+EOF_HUNK
+Indented headings are allowed, as long as the indent is no more than 3 spaces.
+
+   ### RIGHT
+
+- something
+- ChangeMe
+EOF_TEST
+
+test_diff_funcname 'markdown: heading non headings' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+# RIGHT
+EOF_HUNK
+Headings can be right next to other lines of the file:
+# RIGHT
+Indents of four or more spaces make a code block:
+
+    # code comment, not heading
+
+If there's no space after the final hash, it's not a heading:
+
+#hashtag
+
+Sequences of more than 6 hashes don't make a heading:
+
+####### over-enthusiastic heading
+
+So the detected heading should be right up at the start of this file.
+
+ChangeMe
+EOF_TEST
diff --git a/t/t4018/matlab-class-definition b/t/t4018/matlab-class-definition
deleted file mode 100644
index 84daedfb4e..0000000000
--- a/t/t4018/matlab-class-definition
+++ /dev/null
@@ -1,5 +0,0 @@
-classdef RIGHT
-    properties
-        ChangeMe
-    end
-end
diff --git a/t/t4018/matlab-class-definition.ctx b/t/t4018/matlab-class-definition.ctx
deleted file mode 100644
index 5dd5b45628..0000000000
--- a/t/t4018/matlab-class-definition.ctx
+++ /dev/null
@@ -1 +0,0 @@
-classdef RIGHT
diff --git a/t/t4018/matlab-function b/t/t4018/matlab-function
deleted file mode 100644
index 897a9b13ff..0000000000
--- a/t/t4018/matlab-function
+++ /dev/null
@@ -1,4 +0,0 @@
-function y = RIGHT()
-x = 5;
-y = ChangeMe + x;
-end
diff --git a/t/t4018/matlab-function.ctx b/t/t4018/matlab-function.ctx
deleted file mode 100644
index 72d2350b13..0000000000
--- a/t/t4018/matlab-function.ctx
+++ /dev/null
@@ -1 +0,0 @@
-function y = RIGHT()
diff --git a/t/t4018/matlab-octave-section-1 b/t/t4018/matlab-octave-section-1
deleted file mode 100644
index 3bb6c4670e..0000000000
--- a/t/t4018/matlab-octave-section-1
+++ /dev/null
@@ -1,3 +0,0 @@
-%%% RIGHT section
-# this is octave script
-ChangeMe = 1;
diff --git a/t/t4018/matlab-octave-section-1.ctx b/t/t4018/matlab-octave-section-1.ctx
deleted file mode 100644
index ca9b349f94..0000000000
--- a/t/t4018/matlab-octave-section-1.ctx
+++ /dev/null
@@ -1 +0,0 @@
-%%% RIGHT section
diff --git a/t/t4018/matlab-octave-section-2 b/t/t4018/matlab-octave-section-2
deleted file mode 100644
index ab2980f7f2..0000000000
--- a/t/t4018/matlab-octave-section-2
+++ /dev/null
@@ -1,3 +0,0 @@
-## RIGHT section
-# this is octave script
-ChangeMe = 1;
diff --git a/t/t4018/matlab-octave-section-2.ctx b/t/t4018/matlab-octave-section-2.ctx
deleted file mode 100644
index 5cbb77faf5..0000000000
--- a/t/t4018/matlab-octave-section-2.ctx
+++ /dev/null
@@ -1 +0,0 @@
-## RIGHT section
diff --git a/t/t4018/matlab-section b/t/t4018/matlab-section
deleted file mode 100644
index 5ea59a5de0..0000000000
--- a/t/t4018/matlab-section
+++ /dev/null
@@ -1,3 +0,0 @@
-%% RIGHT section
-% this is understood by both matlab and octave
-ChangeMe = 1;
diff --git a/t/t4018/matlab-section.ctx b/t/t4018/matlab-section.ctx
deleted file mode 100644
index e83fee6f4d..0000000000
--- a/t/t4018/matlab-section.ctx
+++ /dev/null
@@ -1 +0,0 @@
-%% RIGHT section
diff --git a/t/t4018/matlab.sh b/t/t4018/matlab.sh
new file mode 100755
index 0000000000..f62289148e
--- /dev/null
+++ b/t/t4018/matlab.sh
@@ -0,0 +1,52 @@
+#!/bin/sh
+#
+# See ../t4018-diff-funcname.sh's test_diff_funcname()
+#
+
+test_diff_funcname 'matlab: class definition' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+classdef RIGHT
+EOF_HUNK
+classdef RIGHT
+    properties
+        ChangeMe
+    end
+end
+EOF_TEST
+
+test_diff_funcname 'matlab: function' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+function y = RIGHT()
+EOF_HUNK
+function y = RIGHT()
+x = 5;
+y = ChangeMe + x;
+end
+EOF_TEST
+
+test_diff_funcname 'matlab: octave section 1' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+%%% RIGHT section
+EOF_HUNK
+%%% RIGHT section
+# this is octave script
+ChangeMe = 1;
+EOF_TEST
+
+test_diff_funcname 'matlab: octave section 2' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+## RIGHT section
+EOF_HUNK
+## RIGHT section
+# this is octave script
+ChangeMe = 1;
+EOF_TEST
+
+test_diff_funcname 'matlab: section' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+%% RIGHT section
+EOF_HUNK
+%% RIGHT section
+% this is understood by both matlab and octave
+ChangeMe = 1;
+EOF_TEST
diff --git a/t/t4018/perl-skip-end-of-heredoc b/t/t4018/perl-skip-end-of-heredoc
deleted file mode 100644
index c22d39b256..0000000000
--- a/t/t4018/perl-skip-end-of-heredoc
+++ /dev/null
@@ -1,8 +0,0 @@
-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-end-of-heredoc.ctx b/t/t4018/perl-skip-end-of-heredoc.ctx
deleted file mode 100644
index c15f4b78bd..0000000000
--- a/t/t4018/perl-skip-end-of-heredoc.ctx
+++ /dev/null
@@ -1 +0,0 @@
-sub RIGHTwithheredocument {
diff --git a/t/t4018/perl-skip-forward-decl b/t/t4018/perl-skip-forward-decl
deleted file mode 100644
index a98cb8bdad..0000000000
--- a/t/t4018/perl-skip-forward-decl
+++ /dev/null
@@ -1,10 +0,0 @@
-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-forward-decl.ctx b/t/t4018/perl-skip-forward-decl.ctx
deleted file mode 100644
index e0c51599ad..0000000000
--- a/t/t4018/perl-skip-forward-decl.ctx
+++ /dev/null
@@ -1 +0,0 @@
-package RIGHT;
diff --git a/t/t4018/perl-skip-sub-in-pod b/t/t4018/perl-skip-sub-in-pod
deleted file mode 100644
index e39f02462e..0000000000
--- a/t/t4018/perl-skip-sub-in-pod
+++ /dev/null
@@ -1,18 +0,0 @@
-=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-skip-sub-in-pod.ctx b/t/t4018/perl-skip-sub-in-pod.ctx
deleted file mode 100644
index abddd76655..0000000000
--- a/t/t4018/perl-skip-sub-in-pod.ctx
+++ /dev/null
@@ -1 +0,0 @@
-=head1 SYNOPSIS_RIGHT
diff --git a/t/t4018/perl-sub-definition b/t/t4018/perl-sub-definition
deleted file mode 100644
index a507d1f645..0000000000
--- a/t/t4018/perl-sub-definition
+++ /dev/null
@@ -1,4 +0,0 @@
-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 330b3df114..0000000000
--- a/t/t4018/perl-sub-definition-kr-brace
+++ /dev/null
@@ -1,4 +0,0 @@
-sub RIGHT
-{
-	print "ChangeMe\n";
-}
diff --git a/t/t4018/perl-sub-definition-kr-brace.ctx b/t/t4018/perl-sub-definition-kr-brace.ctx
deleted file mode 100644
index 7e5aee5cde..0000000000
--- a/t/t4018/perl-sub-definition-kr-brace.ctx
+++ /dev/null
@@ -1 +0,0 @@
-sub RIGHT
diff --git a/t/t4018/perl-sub-definition.ctx b/t/t4018/perl-sub-definition.ctx
deleted file mode 100644
index d49a63598e..0000000000
--- a/t/t4018/perl-sub-definition.ctx
+++ /dev/null
@@ -1 +0,0 @@
-sub RIGHT {
diff --git a/t/t4018/perl.sh b/t/t4018/perl.sh
new file mode 100755
index 0000000000..ac8fff7417
--- /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
diff --git a/t/t4018/php-abstract-class b/t/t4018/php-abstract-class
deleted file mode 100644
index 5213e12494..0000000000
--- a/t/t4018/php-abstract-class
+++ /dev/null
@@ -1,4 +0,0 @@
-abstract class RIGHT
-{
-    const FOO = 'ChangeMe';
-}
diff --git a/t/t4018/php-abstract-class.ctx b/t/t4018/php-abstract-class.ctx
deleted file mode 100644
index f572d2129b..0000000000
--- a/t/t4018/php-abstract-class.ctx
+++ /dev/null
@@ -1 +0,0 @@
-abstract class RIGHT
diff --git a/t/t4018/php-abstract-method b/t/t4018/php-abstract-method
deleted file mode 100644
index ce215df75a..0000000000
--- a/t/t4018/php-abstract-method
+++ /dev/null
@@ -1,7 +0,0 @@
-abstract class Klass
-{
-    abstract public function RIGHT(): ?string
-    {
-        return 'ChangeMe';
-    }
-}
diff --git a/t/t4018/php-abstract-method.ctx b/t/t4018/php-abstract-method.ctx
deleted file mode 100644
index 14cb6df42e..0000000000
--- a/t/t4018/php-abstract-method.ctx
+++ /dev/null
@@ -1 +0,0 @@
-abstract public function RIGHT(): ?string
diff --git a/t/t4018/php-class b/t/t4018/php-class
deleted file mode 100644
index 7785b6303c..0000000000
--- a/t/t4018/php-class
+++ /dev/null
@@ -1,4 +0,0 @@
-class RIGHT
-{
-    const FOO = 'ChangeMe';
-}
diff --git a/t/t4018/php-class.ctx b/t/t4018/php-class.ctx
deleted file mode 100644
index 54bff816d6..0000000000
--- a/t/t4018/php-class.ctx
+++ /dev/null
@@ -1 +0,0 @@
-class RIGHT
diff --git a/t/t4018/php-final-class b/t/t4018/php-final-class
deleted file mode 100644
index 69f5710552..0000000000
--- a/t/t4018/php-final-class
+++ /dev/null
@@ -1,4 +0,0 @@
-final class RIGHT
-{
-    const FOO = 'ChangeMe';
-}
diff --git a/t/t4018/php-final-class.ctx b/t/t4018/php-final-class.ctx
deleted file mode 100644
index 4d59fb749b..0000000000
--- a/t/t4018/php-final-class.ctx
+++ /dev/null
@@ -1 +0,0 @@
-final class RIGHT
diff --git a/t/t4018/php-final-method b/t/t4018/php-final-method
deleted file mode 100644
index 537fb8ad9a..0000000000
--- a/t/t4018/php-final-method
+++ /dev/null
@@ -1,7 +0,0 @@
-class Klass
-{
-    final public function RIGHT(): string
-    {
-        return 'ChangeMe';
-    }
-}
diff --git a/t/t4018/php-final-method.ctx b/t/t4018/php-final-method.ctx
deleted file mode 100644
index b7da8f8082..0000000000
--- a/t/t4018/php-final-method.ctx
+++ /dev/null
@@ -1 +0,0 @@
-final public function RIGHT(): string
diff --git a/t/t4018/php-function b/t/t4018/php-function
deleted file mode 100644
index 35717c51c3..0000000000
--- a/t/t4018/php-function
+++ /dev/null
@@ -1,4 +0,0 @@
-function RIGHT()
-{
-    return 'ChangeMe';
-}
diff --git a/t/t4018/php-function.ctx b/t/t4018/php-function.ctx
deleted file mode 100644
index c5f3e55302..0000000000
--- a/t/t4018/php-function.ctx
+++ /dev/null
@@ -1 +0,0 @@
-function RIGHT()
diff --git a/t/t4018/php-interface b/t/t4018/php-interface
deleted file mode 100644
index 86b49ad5d9..0000000000
--- a/t/t4018/php-interface
+++ /dev/null
@@ -1,4 +0,0 @@
-interface RIGHT
-{
-    public function foo($ChangeMe);
-}
diff --git a/t/t4018/php-interface.ctx b/t/t4018/php-interface.ctx
deleted file mode 100644
index a45fa0532a..0000000000
--- a/t/t4018/php-interface.ctx
+++ /dev/null
@@ -1 +0,0 @@
-interface RIGHT
diff --git a/t/t4018/php-method b/t/t4018/php-method
deleted file mode 100644
index 03af1a6d9d..0000000000
--- a/t/t4018/php-method
+++ /dev/null
@@ -1,7 +0,0 @@
-class Klass
-{
-    public static function RIGHT()
-    {
-        return 'ChangeMe';
-    }
-}
diff --git a/t/t4018/php-method.ctx b/t/t4018/php-method.ctx
deleted file mode 100644
index eb1659ff9f..0000000000
--- a/t/t4018/php-method.ctx
+++ /dev/null
@@ -1 +0,0 @@
-public static function RIGHT()
diff --git a/t/t4018/php-trait b/t/t4018/php-trait
deleted file mode 100644
index 65b8c82a61..0000000000
--- a/t/t4018/php-trait
+++ /dev/null
@@ -1,7 +0,0 @@
-trait RIGHT
-{
-    public function foo($ChangeMe)
-    {
-        return 'foo';
-    }
-}
diff --git a/t/t4018/php-trait.ctx b/t/t4018/php-trait.ctx
deleted file mode 100644
index 57aa4c6267..0000000000
--- a/t/t4018/php-trait.ctx
+++ /dev/null
@@ -1 +0,0 @@
-trait RIGHT
diff --git a/t/t4018/php.sh b/t/t4018/php.sh
new file mode 100755
index 0000000000..e0ccb2277b
--- /dev/null
+++ b/t/t4018/php.sh
@@ -0,0 +1,106 @@
+#!/bin/sh
+#
+# See ../t4018-diff-funcname.sh's test_diff_funcname()
+#
+
+test_diff_funcname 'php: abstract class' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+abstract class RIGHT
+EOF_HUNK
+abstract class RIGHT
+{
+    const FOO = 'ChangeMe';
+}
+EOF_TEST
+
+test_diff_funcname 'php: abstract method' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+abstract public function RIGHT(): ?string
+EOF_HUNK
+abstract class Klass
+{
+    abstract public function RIGHT(): ?string
+    {
+        return 'ChangeMe';
+    }
+}
+EOF_TEST
+
+test_diff_funcname 'php: class' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+class RIGHT
+EOF_HUNK
+class RIGHT
+{
+    const FOO = 'ChangeMe';
+}
+EOF_TEST
+
+test_diff_funcname 'php: final class' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+final class RIGHT
+EOF_HUNK
+final class RIGHT
+{
+    const FOO = 'ChangeMe';
+}
+EOF_TEST
+
+test_diff_funcname 'php: final method' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+final public function RIGHT(): string
+EOF_HUNK
+class Klass
+{
+    final public function RIGHT(): string
+    {
+        return 'ChangeMe';
+    }
+}
+EOF_TEST
+
+test_diff_funcname 'php: function' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+function RIGHT()
+EOF_HUNK
+function RIGHT()
+{
+    return 'ChangeMe';
+}
+EOF_TEST
+
+test_diff_funcname 'php: interface' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+interface RIGHT
+EOF_HUNK
+interface RIGHT
+{
+    public function foo($ChangeMe);
+}
+EOF_TEST
+
+test_diff_funcname 'php: method' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+public static function RIGHT()
+EOF_HUNK
+class Klass
+{
+    public static function RIGHT()
+    {
+        return 'ChangeMe';
+    }
+}
+EOF_TEST
+
+test_diff_funcname 'php: trait' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+trait RIGHT
+EOF_HUNK
+trait RIGHT
+{
+    public function foo($ChangeMe)
+    {
+        return 'foo';
+    }
+}
+EOF_TEST
diff --git a/t/t4018/python-async-def b/t/t4018/python-async-def
deleted file mode 100644
index 87640e03d2..0000000000
--- a/t/t4018/python-async-def
+++ /dev/null
@@ -1,4 +0,0 @@
-async def RIGHT(pi: int = 3.14):
-    while True:
-        break
-    return ChangeMe()
diff --git a/t/t4018/python-async-def.ctx b/t/t4018/python-async-def.ctx
deleted file mode 100644
index 468c548bbe..0000000000
--- a/t/t4018/python-async-def.ctx
+++ /dev/null
@@ -1 +0,0 @@
-async def RIGHT(pi: int = 3.14):
diff --git a/t/t4018/python-class b/t/t4018/python-class
deleted file mode 100644
index ba9e741430..0000000000
--- a/t/t4018/python-class
+++ /dev/null
@@ -1,4 +0,0 @@
-class RIGHT(int, str):
-    # comment
-    # another comment
-    # ChangeMe
diff --git a/t/t4018/python-class.ctx b/t/t4018/python-class.ctx
deleted file mode 100644
index a40b755e29..0000000000
--- a/t/t4018/python-class.ctx
+++ /dev/null
@@ -1 +0,0 @@
-class RIGHT(int, str):
diff --git a/t/t4018/python-def b/t/t4018/python-def
deleted file mode 100644
index e50b31b0ad..0000000000
--- a/t/t4018/python-def
+++ /dev/null
@@ -1,4 +0,0 @@
-def RIGHT(pi: int = 3.14):
-    while True:
-        break
-    return ChangeMe()
diff --git a/t/t4018/python-def.ctx b/t/t4018/python-def.ctx
deleted file mode 100644
index a1a9cbad63..0000000000
--- a/t/t4018/python-def.ctx
+++ /dev/null
@@ -1 +0,0 @@
-def RIGHT(pi: int = 3.14):
diff --git a/t/t4018/python-indented-async-def b/t/t4018/python-indented-async-def
deleted file mode 100644
index f5d03258af..0000000000
--- a/t/t4018/python-indented-async-def
+++ /dev/null
@@ -1,7 +0,0 @@
-class Foo:
-    async def RIGHT(self, x: int):
-        return [
-            1,
-            2,
-            ChangeMe,
-        ]
diff --git a/t/t4018/python-indented-async-def.ctx b/t/t4018/python-indented-async-def.ctx
deleted file mode 100644
index d393620a1e..0000000000
--- a/t/t4018/python-indented-async-def.ctx
+++ /dev/null
@@ -1 +0,0 @@
-async def RIGHT(self, x: int):
diff --git a/t/t4018/python-indented-class b/t/t4018/python-indented-class
deleted file mode 100644
index 19b4f35c4c..0000000000
--- a/t/t4018/python-indented-class
+++ /dev/null
@@ -1,5 +0,0 @@
-if TYPE_CHECKING:
-    class RIGHT:
-        # comment
-        # another comment
-        # ChangeMe
diff --git a/t/t4018/python-indented-class.ctx b/t/t4018/python-indented-class.ctx
deleted file mode 100644
index 0881c84dba..0000000000
--- a/t/t4018/python-indented-class.ctx
+++ /dev/null
@@ -1 +0,0 @@
-class RIGHT:
diff --git a/t/t4018/python-indented-def b/t/t4018/python-indented-def
deleted file mode 100644
index 208fbadd2b..0000000000
--- a/t/t4018/python-indented-def
+++ /dev/null
@@ -1,7 +0,0 @@
-class Foo:
-    def RIGHT(self, x: int):
-        return [
-            1,
-            2,
-            ChangeMe,
-        ]
diff --git a/t/t4018/python-indented-def.ctx b/t/t4018/python-indented-def.ctx
deleted file mode 100644
index 6e5a44b391..0000000000
--- a/t/t4018/python-indented-def.ctx
+++ /dev/null
@@ -1 +0,0 @@
-def RIGHT(self, x: int):
diff --git a/t/t4018/python.sh b/t/t4018/python.sh
new file mode 100755
index 0000000000..ecb5736d57
--- /dev/null
+++ b/t/t4018/python.sh
@@ -0,0 +1,71 @@
+#!/bin/sh
+#
+# See ../t4018-diff-funcname.sh's test_diff_funcname()
+#
+
+test_diff_funcname 'python: async def' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+async def RIGHT(pi: int = 3.14):
+EOF_HUNK
+async def RIGHT(pi: int = 3.14):
+    while True:
+        break
+    return ChangeMe()
+EOF_TEST
+
+test_diff_funcname 'python: class' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+class RIGHT(int, str):
+EOF_HUNK
+class RIGHT(int, str):
+    # comment
+    # another comment
+    # ChangeMe
+EOF_TEST
+
+test_diff_funcname 'python: def' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+def RIGHT(pi: int = 3.14):
+EOF_HUNK
+def RIGHT(pi: int = 3.14):
+    while True:
+        break
+    return ChangeMe()
+EOF_TEST
+
+test_diff_funcname 'python: indented async def' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+async def RIGHT(self, x: int):
+EOF_HUNK
+class Foo:
+    async def RIGHT(self, x: int):
+        return [
+            1,
+            2,
+            ChangeMe,
+        ]
+EOF_TEST
+
+test_diff_funcname 'python: indented class' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+class RIGHT:
+EOF_HUNK
+if TYPE_CHECKING:
+    class RIGHT:
+        # comment
+        # another comment
+        # ChangeMe
+EOF_TEST
+
+test_diff_funcname 'python: indented def' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+def RIGHT(self, x: int):
+EOF_HUNK
+class Foo:
+    def RIGHT(self, x: int):
+        return [
+            1,
+            2,
+            ChangeMe,
+        ]
+EOF_TEST
diff --git a/t/t4018/rust-fn b/t/t4018/rust-fn
deleted file mode 100644
index cbe02155f1..0000000000
--- a/t/t4018/rust-fn
+++ /dev/null
@@ -1,5 +0,0 @@
-pub(self) fn RIGHT<T>(x: &[T]) where T: Debug {
-    let _ = x;
-    // a comment
-    let a = ChangeMe;
-}
diff --git a/t/t4018/rust-fn.ctx b/t/t4018/rust-fn.ctx
deleted file mode 100644
index baa37cf253..0000000000
--- a/t/t4018/rust-fn.ctx
+++ /dev/null
@@ -1 +0,0 @@
-pub(self) fn RIGHT<T>(x: &[T]) where T: Debug {
diff --git a/t/t4018/rust-impl b/t/t4018/rust-impl
deleted file mode 100644
index 09df3cd93b..0000000000
--- a/t/t4018/rust-impl
+++ /dev/null
@@ -1,5 +0,0 @@
-impl<'a, T: AsRef<[u8]>>  std::RIGHT for Git<'a> {
-
-    pub fn ChangeMe(&self) -> () {
-    }
-}
diff --git a/t/t4018/rust-impl.ctx b/t/t4018/rust-impl.ctx
deleted file mode 100644
index 5344c35f3f..0000000000
--- a/t/t4018/rust-impl.ctx
+++ /dev/null
@@ -1 +0,0 @@
-impl<'a, T: AsRef<[u8]>>  std::RIGHT for Git<'a> {
diff --git a/t/t4018/rust-macro-rules b/t/t4018/rust-macro-rules
deleted file mode 100644
index ec610c5b62..0000000000
--- a/t/t4018/rust-macro-rules
+++ /dev/null
@@ -1,6 +0,0 @@
-macro_rules! RIGHT {
-    () => {
-        // a comment
-        let x = ChangeMe;
-    };
-}
diff --git a/t/t4018/rust-macro-rules.ctx b/t/t4018/rust-macro-rules.ctx
deleted file mode 100644
index 7520463aa0..0000000000
--- a/t/t4018/rust-macro-rules.ctx
+++ /dev/null
@@ -1 +0,0 @@
-macro_rules! RIGHT {
diff --git a/t/t4018/rust-struct b/t/t4018/rust-struct
deleted file mode 100644
index 76aff1c0d8..0000000000
--- a/t/t4018/rust-struct
+++ /dev/null
@@ -1,5 +0,0 @@
-#[derive(Debug)]
-pub(super) struct RIGHT<'a> {
-    name: &'a str,
-    age: ChangeMe,
-}
diff --git a/t/t4018/rust-struct.ctx b/t/t4018/rust-struct.ctx
deleted file mode 100644
index c1e09dc808..0000000000
--- a/t/t4018/rust-struct.ctx
+++ /dev/null
@@ -1 +0,0 @@
-pub(super) struct RIGHT<'a> {
diff --git a/t/t4018/rust-trait b/t/t4018/rust-trait
deleted file mode 100644
index ea397f09ed..0000000000
--- a/t/t4018/rust-trait
+++ /dev/null
@@ -1,5 +0,0 @@
-unsafe trait RIGHT<T> {
-    fn len(&self) -> u32;
-    fn ChangeMe(&self, n: u32) -> T;
-    fn iter<F>(&self, f: F) where F: Fn(T);
-}
diff --git a/t/t4018/rust-trait.ctx b/t/t4018/rust-trait.ctx
deleted file mode 100644
index 6af803db29..0000000000
--- a/t/t4018/rust-trait.ctx
+++ /dev/null
@@ -1 +0,0 @@
-unsafe trait RIGHT<T> {
diff --git a/t/t4018/rust.sh b/t/t4018/rust.sh
new file mode 100755
index 0000000000..ba018c6b95
--- /dev/null
+++ b/t/t4018/rust.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+#
+# See ../t4018-diff-funcname.sh's test_diff_funcname()
+#
+
+test_diff_funcname 'rust: fn' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+pub(self) fn RIGHT<T>(x: &[T]) where T: Debug {
+EOF_HUNK
+pub(self) fn RIGHT<T>(x: &[T]) where T: Debug {
+    let _ = x;
+    // a comment
+    let a = ChangeMe;
+}
+EOF_TEST
+
+test_diff_funcname 'rust: impl' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+impl<'a, T: AsRef<[u8]>>  std::RIGHT for Git<'a> {
+EOF_HUNK
+impl<'a, T: AsRef<[u8]>>  std::RIGHT for Git<'a> {
+
+    pub fn ChangeMe(&self) -> () {
+    }
+}
+EOF_TEST
+
+test_diff_funcname 'rust: macro rules' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+macro_rules! RIGHT {
+EOF_HUNK
+macro_rules! RIGHT {
+    () => {
+        // a comment
+        let x = ChangeMe;
+    };
+}
+EOF_TEST
+
+test_diff_funcname 'rust: struct' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+pub(super) struct RIGHT<'a> {
+EOF_HUNK
+#[derive(Debug)]
+pub(super) struct RIGHT<'a> {
+    name: &'a str,
+    age: ChangeMe,
+}
+EOF_TEST
+
+test_diff_funcname 'rust: trait' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+unsafe trait RIGHT<T> {
+EOF_HUNK
+unsafe trait RIGHT<T> {
+    fn len(&self) -> u32;
+    fn ChangeMe(&self, n: u32) -> T;
+    fn iter<F>(&self, f: F) where F: Fn(T);
+}
+EOF_TEST
-- 
2.30.0.284.gd98b1dd5eaa7


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

* [PATCH 09/20] blame tests: don't rely on t/t4018/ directory
  2021-02-14 18:25     ` Ævar Arnfjörð Bjarmason
                         ` (8 preceding siblings ...)
  2021-02-15  0:52       ` [PATCH 08/20] userdiff tests: rewrite hunk header test infrastructure Ævar Arnfjörð Bjarmason
@ 2021-02-15  0:52       ` Ævar Arnfjörð Bjarmason
  2021-02-15  0:52       ` [PATCH 10/20] userdiff tests: move custom patterns into one test file Ævar Arnfjörð Bjarmason
                         ` (11 subsequent siblings)
  21 siblings, 0 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15  0:52 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers,
	Ævar Arnfjörð Bjarmason

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 refactor that directory, 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 <avarab@gmail.com>
---
 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 29ce89090d..04a2c58594 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


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

* [PATCH 10/20] userdiff tests: move custom patterns into one test file
  2021-02-14 18:25     ` Ævar Arnfjörð Bjarmason
                         ` (9 preceding siblings ...)
  2021-02-15  0:52       ` [PATCH 09/20] blame tests: don't rely on t/t4018/ directory Ævar Arnfjörð Bjarmason
@ 2021-02-15  0:52       ` Ævar Arnfjörð Bjarmason
  2021-02-15  0:52       ` [PATCH 11/20] userdiff tests: remove hack for "RIGHT" token Ævar Arnfjörð Bjarmason
                         ` (10 subsequent siblings)
  21 siblings, 0 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15  0:52 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers,
	Ævar Arnfjörð Bjarmason

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 <avarab@gmail.com>
---
 t/t4018-diff-funcname.sh | 16 +-------
 t/t4018/custom.sh        | 84 ++++++++++++++++++++++++++++++++++++++++
 t/t4018/custom1.sh       | 27 -------------
 t/t4018/custom2.sh       | 18 ---------
 t/t4018/custom3.sh       | 27 -------------
 5 files changed, 85 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 3ff34c13d7..3e4c07e42b 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -12,18 +12,6 @@ test_expect_success 'setup' '
 	test_file_not_empty builtin-drivers &&
 	builtin_drivers=$(cat 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
@@ -31,9 +19,7 @@ test_expect_success 'setup' '
 
 diffpatterns="
 	$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 0000000000..69f1f7339f
--- /dev/null
+++ b/t/t4018/custom.sh
@@ -0,0 +1,84 @@
+#!/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
+
+test_expect_success 'custom: teardown' '
+	test_unconfig diff.custom.funcname &&
+	test_unconfig diff.custom.xfuncname
+'
diff --git a/t/t4018/custom1.sh b/t/t4018/custom1.sh
deleted file mode 100755
index f8bbccadb4..0000000000
--- 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 c68421f788..0000000000
--- 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 07c5c134ff..0000000000
--- 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


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

* [PATCH 11/20] userdiff tests: remove hack for "RIGHT" token
  2021-02-14 18:25     ` Ævar Arnfjörð Bjarmason
                         ` (10 preceding siblings ...)
  2021-02-15  0:52       ` [PATCH 10/20] userdiff tests: move custom patterns into one test file Ævar Arnfjörð Bjarmason
@ 2021-02-15  0:52       ` Ævar Arnfjörð Bjarmason
  2021-02-15  0:52       ` [PATCH 12/20] userdiff: match "package" in diff=golang Ævar Arnfjörð Bjarmason
                         ` (9 subsequent siblings)
  21 siblings, 0 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15  0:52 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers,
	Ævar Arnfjörð Bjarmason

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 <avarab@gmail.com>
---
 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 69f1f7339f..20abb38451 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


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

* [PATCH 12/20] userdiff: match "package" in diff=golang
  2021-02-14 18:25     ` Ævar Arnfjörð Bjarmason
                         ` (11 preceding siblings ...)
  2021-02-15  0:52       ` [PATCH 11/20] userdiff tests: remove hack for "RIGHT" token Ævar Arnfjörð Bjarmason
@ 2021-02-15  0:52       ` Ævar Arnfjörð Bjarmason
  2021-02-15  0:52       ` [PATCH 13/20] userdiff tests + docs: document & test "diff.<driver>.x?funcname" Ævar Arnfjörð Bjarmason
                         ` (8 subsequent siblings)
  21 siblings, 0 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15  0:52 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers,
	Ævar Arnfjörð Bjarmason

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.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 t/t4018/golang.sh | 10 ++++++++++
 userdiff.c        |  2 ++
 2 files changed, 12 insertions(+)

diff --git a/t/t4018/golang.sh b/t/t4018/golang.sh
index bf22f58c12..cdf9d6f8aa 100755
--- a/t/t4018/golang.sh
+++ b/t/t4018/golang.sh
@@ -3,6 +3,16 @@
 # See ../t4018-diff-funcname.sh's test_diff_funcname()
 #
 
+test_diff_funcname 'golang: package' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+package main
+EOF_HUNK
+package main
+
+import "fmt"
+// ChangeMe
+EOF_TEST
+
 test_diff_funcname 'golang: complex function' \
 	8<<\EOF_HUNK 9<<\EOF_TEST
 func (t *Test) RIGHT(a Type) (Type, error) {
diff --git a/userdiff.c b/userdiff.c
index 92b5a97e12..d99b488700 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -124,6 +124,8 @@ IPATTERN("fortran",
 IPATTERN("fountain", "^((\\.[^.]|(int|ext|est|int\\.?/ext|i/e)[. ]).*)$",
 	 "[^ \t-]+"),
 PATTERNS("golang",
+	 /* Packages */
+	 "^[ \t]*(package[ \t]*(.*))\n"
 	 /* Functions */
 	 "^[ \t]*(func[ \t]*.*(\\{[ \t]*)?)\n"
 	 /* Structs and interfaces */
-- 
2.30.0.284.gd98b1dd5eaa7


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

* [PATCH 13/20] userdiff tests + docs: document & test "diff.<driver>.x?funcname"
  2021-02-14 18:25     ` Ævar Arnfjörð Bjarmason
                         ` (12 preceding siblings ...)
  2021-02-15  0:52       ` [PATCH 12/20] userdiff: match "package" in diff=golang Ævar Arnfjörð Bjarmason
@ 2021-02-15  0:52       ` Ævar Arnfjörð Bjarmason
  2021-02-15  3:16         ` Eric Sunshine
  2021-02-15  0:52       ` [PATCH 14/20] gitattributes doc: reword discussion of built-in userdiff patterns Ævar Arnfjörð Bjarmason
                         ` (7 subsequent siblings)
  21 siblings, 1 reply; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15  0:52 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers,
	Ævar Arnfjörð Bjarmason

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 <avarab@gmail.com>
---
 Documentation/config/diff.txt | 12 +++++++++++
 t/t4018/custom.sh             | 40 +++++++++++++++++++++++++++++++++++
 2 files changed, 52 insertions(+)

diff --git a/Documentation/config/diff.txt b/Documentation/config/diff.txt
index 2d3331f55c..5fce8021de 100644
--- a/Documentation/config/diff.txt
+++ b/Documentation/config/diff.txt
@@ -153,10 +153,22 @@ 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 20abb38451..b68d96a8af 100755
--- a/t/t4018/custom.sh
+++ b/t/t4018/custom.sh
@@ -78,6 +78,46 @@ 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: teardown' '
+	test_unconfig diff.custom.funcname &&
+	test_unconfig diff.custom.xfuncname
+'
+
+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
+
 test_expect_success 'custom: teardown' '
 	test_unconfig diff.custom.funcname &&
 	test_unconfig diff.custom.xfuncname
-- 
2.30.0.284.gd98b1dd5eaa7


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

* [PATCH 14/20] gitattributes doc: reword discussion of built-in userdiff patterns
  2021-02-14 18:25     ` Ævar Arnfjörð Bjarmason
                         ` (13 preceding siblings ...)
  2021-02-15  0:52       ` [PATCH 13/20] userdiff tests + docs: document & test "diff.<driver>.x?funcname" Ævar Arnfjörð Bjarmason
@ 2021-02-15  0:52       ` Ævar Arnfjörð Bjarmason
  2021-02-15  3:26         ` Eric Sunshine
  2021-02-15  0:52       ` [PATCH 15/20] gitattributes doc: document multi-line " Ævar Arnfjörð Bjarmason
                         ` (6 subsequent siblings)
  21 siblings, 1 reply; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15  0:52 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers,
	Ævar Arnfjörð Bjarmason

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.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 Documentation/gitattributes.txt | 16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)

diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index e84e104f93..90992e2136 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 the "diff.tex.xfuncname"
+discussed above in your configuration file, but if present it'll
+override the built-in pattern.
+
+You still need to enable built-in patterns with the the attribute
+mechanism, via `.gitattributes`).
+
+The following built in patterns are available:
 
 - `ada` suitable for source code in the Ada language.
 
-- 
2.30.0.284.gd98b1dd5eaa7


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

* [PATCH 15/20] gitattributes doc: document multi-line userdiff patterns
  2021-02-14 18:25     ` Ævar Arnfjörð Bjarmason
                         ` (14 preceding siblings ...)
  2021-02-15  0:52       ` [PATCH 14/20] gitattributes doc: reword discussion of built-in userdiff patterns Ævar Arnfjörð Bjarmason
@ 2021-02-15  0:52       ` Ævar Arnfjörð Bjarmason
  2021-02-15  3:16         ` Chris Torek
  2021-02-15  3:36         ` Eric Sunshine
  2021-02-15  0:52       ` [PATCH 16/20] userdiff tests: remove "funcname" from custom3 test Ævar Arnfjörð Bjarmason
                         ` (5 subsequent siblings)
  21 siblings, 2 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15  0:52 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers,
	Ævar Arnfjörð Bjarmason

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 <avarab@gmail.com>
---
 Documentation/gitattributes.txt | 17 ++++++++++++++++
 t/t4018/custom.sh               | 35 +++++++++++++++++++++++++++++++++
 2 files changed, 52 insertions(+)

diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index 90992e2136..225c17b90d 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -794,6 +794,23 @@ 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.
 
+Multiple patterns can be supplied by seperating them with
+newlines. They will be matched one at a time and are compiled as
+separate patterns, and thus the first capture in each such pattern is
+`$1`, see further discussion of captures below.
+
+Patterns that begin with "!" are negated (to match a literal "!" at
+the start of a line use e.g. "[!]"). A matching negated pattern will
+cause the matching line to be skipped. Use it to blacklist otherwise
+matching non-negated patterns. The last pattern must not be negated,
+we'll error out if that's the case.
+
+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. A more
 advanced version of the `tex` pattern discussed above is one of them.
 
diff --git a/t/t4018/custom.sh b/t/t4018/custom.sh
index b68d96a8af..cccf468c3a 100755
--- a/t/t4018/custom.sh
+++ b/t/t4018/custom.sh
@@ -122,3 +122,38 @@ test_expect_success 'custom: teardown' '
 	test_unconfig diff.custom.funcname &&
 	test_unconfig diff.custom.xfuncname
 '
+
+test_expect_success 'custom: negation syntax, ! is magic' '
+	git config diff.custom.xfuncname "!negation
+line"
+'
+
+test_diff_funcname 'custom: config precedence' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+line
+EOF_HUNK
+line
+!negation
+
+ChangeMe
+
+baz
+EOF_TEST
+
+test_expect_success 'custom: negation syntax, use [!] to override ! magic' '
+	git config diff.custom.xfuncname "[!]negation
+line"
+'
+
+test_diff_funcname 'custom: config precedence' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+!negation
+EOF_HUNK
+line
+!negation
+
+ChangeMe
+
+baz
+EOF_TEST
+
-- 
2.30.0.284.gd98b1dd5eaa7


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

* [PATCH 16/20] userdiff tests: remove "funcname" from custom3 test
  2021-02-14 18:25     ` Ævar Arnfjörð Bjarmason
                         ` (15 preceding siblings ...)
  2021-02-15  0:52       ` [PATCH 15/20] gitattributes doc: document multi-line " Ævar Arnfjörð Bjarmason
@ 2021-02-15  0:52       ` Ævar Arnfjörð Bjarmason
  2021-02-15  0:52       ` [PATCH 17/20] userdiff tests: factor out test_diff_funcname() logic Ævar Arnfjörð Bjarmason
                         ` (4 subsequent siblings)
  21 siblings, 0 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15  0:52 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers,
	Ævar Arnfjörð Bjarmason

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 <avarab@gmail.com>
---
 t/t4018/custom.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/t/t4018/custom.sh b/t/t4018/custom.sh
index cccf468c3a..81a68aa332 100755
--- a/t/t4018/custom.sh
+++ b/t/t4018/custom.sh
@@ -51,7 +51,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).*)$"
 '
 
@@ -133,6 +132,7 @@ test_diff_funcname 'custom: config precedence' \
 line
 EOF_HUNK
 line
+
 !negation
 
 ChangeMe
-- 
2.30.0.284.gd98b1dd5eaa7


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

* [PATCH 17/20] userdiff tests: factor out test_diff_funcname() logic
  2021-02-14 18:25     ` Ævar Arnfjörð Bjarmason
                         ` (16 preceding siblings ...)
  2021-02-15  0:52       ` [PATCH 16/20] userdiff tests: remove "funcname" from custom3 test Ævar Arnfjörð Bjarmason
@ 2021-02-15  0:52       ` Ævar Arnfjörð Bjarmason
  2021-02-15  0:52       ` [PATCH 18/20] userdiff tests: test hunk headers on accumulated files Ævar Arnfjörð Bjarmason
                         ` (3 subsequent siblings)
  21 siblings, 0 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15  0:52 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers,
	Ævar Arnfjörð Bjarmason

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 <avarab@gmail.com>
---
 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 3e4c07e42b..7a830ec57f 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -47,6 +47,17 @@ test_expect_success 'last regexp must not be negated' '
 	test_i18ngrep ": Last expression must not be negated:" msg
 '
 
+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" <$file
+}
+
 test_diff_funcname () {
 	desc=$1
 	cat <&8 >arg.header &&
@@ -57,13 +68,12 @@ 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"
 	' &&
 
 	test_expect_success "$desc" '
 		git diff -U1 "$what" >diff &&
-		sed -n -e "s/^.*@@\( \|$\)//p" <diff >actual &&
+		last_diff_context_line diff >actual &&
 		test_cmp expected actual
 	'
 }
-- 
2.30.0.284.gd98b1dd5eaa7


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

* [PATCH 18/20] userdiff tests: test hunk headers on accumulated files
  2021-02-14 18:25     ` Ævar Arnfjörð Bjarmason
                         ` (17 preceding siblings ...)
  2021-02-15  0:52       ` [PATCH 17/20] userdiff tests: factor out test_diff_funcname() logic Ævar Arnfjörð Bjarmason
@ 2021-02-15  0:52       ` Ævar Arnfjörð Bjarmason
  2021-02-15  0:52       ` [PATCH 19/20] userdiff tests: test hunk header selection with -U0 Ævar Arnfjörð Bjarmason
                         ` (2 subsequent siblings)
  21 siblings, 0 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15  0:52 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers,
	Ævar Arnfjörð Bjarmason

The existing tests in "t/t4018/" are unrealistic in that they're all
setting up small few-line isolated test cases with one thing we could
match as a hunk header, right above the one change in the file.

Expand those tests by accumulating changes within the same file type
in the "test_diff_funcname" function. So e.g. for "bash" we'll end up
a "bash.acc" file with 15 s/ChangeMe/IWasChanged/ changes.

This stress tests whether the hunk header selection will "jump across"
to an earlier change because the match for that is greedier.

As it turns out we had one false positive in "t/t4018/cpp.sh" and
"t4018/matlab.sh" because of how the tests were structured, we must
always give the "ChangeMe" line at least one line of separation from
the header, since it was at the end of those tests we'd select the
"wrong" header. Let's adjust the spacing to compensate.

So in the end we found nothing of interest here, regardless, I think
it is useful to continue to test in this mode. It's likely to aid in
finding bugs in combinations of our positive and negative matching as
we add more built-in patterns.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 t/t4018-diff-funcname.sh | 19 +++++++++++++++++++
 t/t4018/cpp.sh           |  1 +
 t/t4018/matlab.sh        |  3 +++
 3 files changed, 23 insertions(+)

diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index 7a830ec57f..0d75d93c69 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -71,10 +71,24 @@ test_diff_funcname () {
 		do_change_me "$what"
 	' &&
 
+	test_expect_success "setup: $desc (accumulated)" '
+		cat arg.test >>arg.tests &&
+		cp arg.tests "$what".acc &&
+		git add "$what".acc &&
+		do_change_me "$what".acc
+	' &&
+
 	test_expect_success "$desc" '
 		git diff -U1 "$what" >diff &&
 		last_diff_context_line diff >actual &&
 		test_cmp expected actual
+	' &&
+
+	test_expect_success "$desc (accumulated)" '
+		git diff -U1 "$what".acc >diff &&
+		last_diff_context_line diff >actual.lines &&
+		tail -n 1 actual.lines >actual &&
+		test_cmp expected actual
 	'
 }
 
@@ -92,6 +106,11 @@ do
 		echo "$what" >arg.what
 	' &&
 
+	test_expect_success "setup: hunk header for $what (accumulated)" '
+		>arg.tests &&
+		echo "$what.acc diff=$what" >>.gitattributes
+	' &&
+
 	. "$test"
 done
 
diff --git a/t/t4018/cpp.sh b/t/t4018/cpp.sh
index 185d40d5ef..e0ab749316 100755
--- a/t/t4018/cpp.sh
+++ b/t/t4018/cpp.sh
@@ -206,6 +206,7 @@ void wrong()
 struct RIGHT_iterator_tag {};
 
 int ChangeMe;
+
 EOF_TEST
 
 test_diff_funcname 'cpp: template function definition' \
diff --git a/t/t4018/matlab.sh b/t/t4018/matlab.sh
index f62289148e..fba410e6f5 100755
--- a/t/t4018/matlab.sh
+++ b/t/t4018/matlab.sh
@@ -31,6 +31,7 @@ EOF_HUNK
 %%% RIGHT section
 # this is octave script
 ChangeMe = 1;
+
 EOF_TEST
 
 test_diff_funcname 'matlab: octave section 2' \
@@ -40,6 +41,7 @@ EOF_HUNK
 ## RIGHT section
 # this is octave script
 ChangeMe = 1;
+
 EOF_TEST
 
 test_diff_funcname 'matlab: section' \
@@ -49,4 +51,5 @@ EOF_HUNK
 %% RIGHT section
 % this is understood by both matlab and octave
 ChangeMe = 1;
+
 EOF_TEST
-- 
2.30.0.284.gd98b1dd5eaa7


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

* [PATCH 19/20] userdiff tests: test hunk header selection with -U0
  2021-02-14 18:25     ` Ævar Arnfjörð Bjarmason
                         ` (18 preceding siblings ...)
  2021-02-15  0:52       ` [PATCH 18/20] userdiff tests: test hunk headers on accumulated files Ævar Arnfjörð Bjarmason
@ 2021-02-15  0:52       ` Ævar Arnfjörð Bjarmason
  2021-02-15  0:52       ` [PATCH 20/20] userdiff tests: assert empty hunk header context on -U<large> Ævar Arnfjörð Bjarmason
  2021-02-16  8:26       ` [PATCH] userdiff: add support for Emacs Lisp Protesilaos Stavrou
  21 siblings, 0 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15  0:52 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers,
	Ævar Arnfjörð Bjarmason

The userdiff tests have used a custom -U1 context since
f12c66b9bb (userdiff/perl: anchor "sub" and "package" patterns on the
left, 2011-05-21). Changing it to -U0 doesn't change the results for
any of the tests, except one.

Let's test for this case explicitly. I.e. that we go "beyond" the
selected context to find our hunk header. In many cases the desired
hunk header is part of the diff itself under -U1.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 t/t4018-diff-funcname.sh | 13 +++++++++++++
 t/t4018/custom.sh        |  1 +
 2 files changed, 14 insertions(+)

diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index 0d75d93c69..94026b8296 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -63,6 +63,7 @@ test_diff_funcname () {
 	cat <&8 >arg.header &&
 	cat <&9 >arg.test &&
 	what=$(cat arg.what) &&
+	arg_diff_U0=$2 &&
 
 	test_expect_success "setup: $desc" '
 		cp arg.test "$what" &&
@@ -84,6 +85,18 @@ test_diff_funcname () {
 		test_cmp expected actual
 	' &&
 
+	test_expect_success "$desc -U0" '
+		git diff -U0 "$what" >diff &&
+		last_diff_context_line diff >actual &&
+		if test -n "$arg_diff_U0"
+		then
+			echo "$arg_diff_U0" >new-expected &&
+			test_cmp new-expected actual
+		else
+			test_cmp expected actual
+		fi
+	' &&
+
 	test_expect_success "$desc (accumulated)" '
 		git diff -U1 "$what".acc >diff &&
 		last_diff_context_line diff >actual.lines &&
diff --git a/t/t4018/custom.sh b/t/t4018/custom.sh
index 81a68aa332..605e2d33ae 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' \
+	'System.out.print(x + " bottles of beer on the wall "' \
 	8<<\EOF_HUNK 9<<\EOF_TEST
 int special, RIGHT;
 EOF_HUNK
-- 
2.30.0.284.gd98b1dd5eaa7


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

* [PATCH 20/20] userdiff tests: assert empty hunk header context on -U<large>
  2021-02-14 18:25     ` Ævar Arnfjörð Bjarmason
                         ` (19 preceding siblings ...)
  2021-02-15  0:52       ` [PATCH 19/20] userdiff tests: test hunk header selection with -U0 Ævar Arnfjörð Bjarmason
@ 2021-02-15  0:52       ` Ævar Arnfjörð Bjarmason
  2021-02-16  8:26       ` [PATCH] userdiff: add support for Emacs Lisp Protesilaos Stavrou
  21 siblings, 0 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15  0:52 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers,
	Ævar Arnfjörð Bjarmason

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 <avarab@gmail.com>
---
 t/t4018-diff-funcname.sh | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index 94026b8296..bd81974dab 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -103,6 +103,14 @@ test_diff_funcname () {
 		tail -n 1 actual.lines >actual &&
 		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 &&
+		echo >blank &&
+		test_cmp blank actual
+	'
 }
 
 for what in $diffpatterns
-- 
2.30.0.284.gd98b1dd5eaa7


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

* Re: [PATCH 05/20] userdiff tests: list builtin drivers via test-tool
  2021-02-15  0:52       ` [PATCH 05/20] userdiff tests: list builtin drivers via test-tool Ævar Arnfjörð Bjarmason
@ 2021-02-15  1:24         ` Eric Sunshine
  0 siblings, 0 replies; 154+ messages in thread
From: Eric Sunshine @ 2021-02-15  1:24 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: Git List, Junio C Hamano, Johannes Sixt, Jeff King,
	Jonathan Nieder, Philippe Blain, Adam Spiers

On Sun, Feb 14, 2021 at 7:56 PM Ævar Arnfjörð Bjarmason
<avarab@gmail.com> wrote:
> Change the userdiff test to list the builtin drivers via the
> test-tool, using the new for_each_userdiff_driver() API function.
> [...]
> 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 <avarab@gmail.com>
> ---
> diff --git a/t/helper/test-userdiff.c b/t/helper/test-userdiff.c
> @@ -0,0 +1,32 @@
> +static int userdiff_list_builtin_drivers_cb(struct userdiff_driver *driver,
> +                                           enum userdiff_driver_type type,
> +                                           void *priv)
> +{
> +       puts(driver->name);
> +       return 0;
> +}

Nit: The word "builtin" in the name of this callback function made me
search the patch for its cousin function which would be used for
custom drivers. It was only after reading on that I discovered that
this one function is used for both builtin and custom drivers. (Caught
me off guard, but not itself worth a re-roll.)

> diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
> @@ -8,6 +8,10 @@ test_description='Test custom diff function name patterns'
>  test_expect_success 'setup' '
> +       test-tool userdiff list-builtin-drivers >builtin-drivers &&
> +       test_file_not_empty builtin-drivers &&
> +       builtin_drivers=$(cat builtin-drivers) &&

Nit: I get why you are using test_file_not_empty() for its diagnostic
value, but it does confuse the reader a bit to see the file
"builtin-drivers" created for no particularly good reason. This could
easily have been:

    builtin_drivers=$(test-tool userdiff list-builtin-drivers) &&
    test -n "$builtin_drivers" &&

Subjective and not worth a re-roll.

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

* Re: [PATCH 07/20] userdiff tests: match full hunk headers
  2021-02-15  0:52       ` [PATCH 07/20] userdiff tests: match full hunk headers Ævar Arnfjörð Bjarmason
@ 2021-02-15  1:35         ` Eric Sunshine
  0 siblings, 0 replies; 154+ messages in thread
From: Eric Sunshine @ 2021-02-15  1:35 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: Git List, Junio C Hamano, Johannes Sixt, Jeff King,
	Jonathan Nieder, Philippe Blain, Adam Spiers

On Sun, Feb 14, 2021 at 7:56 PM Ævar Arnfjörð Bjarmason
<avarab@gmail.com> wrote:
> [...]
> Let's bring back coverage for that by adding corresponding *.ctx
> files, this has the added advantage that we're doing a "test_cmp", so
> when we have failures it's just a non-zero exit code from "grep",
> we'll actually have something meaningful in the "-v" output.
> [...]
> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
> diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
> @@ -81,11 +81,12 @@ test_expect_success 'setup hunk header tests' '
> +for i in $(git ls-files -- ':!*.ctx')
>  do
>         test_expect_success "hunk header: $i" "
> +               git diff -U1 $i >diff &&
> +               sed -n -e 's/^.*@@\( \|$\)//p' <diff >ctx &&
> +               test_cmp $i.ctx ctx
>         "
>  done

If I'm reading this correctly, you're simply stripping off all the
leading `@@ blah @@` stuff...

> diff --git a/t/t4018/README b/t/t4018/README
> @@ -1,15 +1,15 @@
> +The text that must appear in the hunk header must contains the word
> +"RIGHT" by convention. The "LANG-whatever.ctx" file contains what we
> +expect to appear in the hunk header. We munged the start of the line
> +to "@@ [...] @@" for ease of not having to hardcode the line numbers
> +and offsets.

...which makes me wonder what this "munging to `@@ [...] @@`" is about.

Is this documentation update wrong or am I misunderstanding?

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

* Re: [PATCH 15/20] gitattributes doc: document multi-line userdiff patterns
  2021-02-15  0:52       ` [PATCH 15/20] gitattributes doc: document multi-line " Ævar Arnfjörð Bjarmason
@ 2021-02-15  3:16         ` Chris Torek
  2021-02-15  3:36         ` Eric Sunshine
  1 sibling, 0 replies; 154+ messages in thread
From: Chris Torek @ 2021-02-15  3:16 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: Git List, Junio C Hamano, Johannes Sixt, Jeff King,
	Jonathan Nieder, Philippe Blain, Adam Spiers

This is extremely trivial, just something to consider if you are
already doing a reroll:

On Sun, Feb 14, 2021 at 4:57 PM Ævar Arnfjörð Bjarmason
<avarab@gmail.com> wrote:
> diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
> index 90992e2136..225c17b90d 100644
> --- a/Documentation/gitattributes.txt
> +++ b/Documentation/gitattributes.txt
> @@ -794,6 +794,23 @@ 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.
>
> +Multiple patterns can be supplied by seperating them with
> +newlines. They will be matched one at a time and are compiled as
> +separate patterns, and thus the first capture in each such pattern is
> +`$1`, see further discussion of captures below.

This is a comma splice (https://en.wikipedia.org/wiki/Comma_splice).
Use a period or semicolon to fix it.

(overall comment, haven't quite finished scanning the series yet but it
looks good!)

Chris

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

* Re: [PATCH 13/20] userdiff tests + docs: document & test "diff.<driver>.x?funcname"
  2021-02-15  0:52       ` [PATCH 13/20] userdiff tests + docs: document & test "diff.<driver>.x?funcname" Ævar Arnfjörð Bjarmason
@ 2021-02-15  3:16         ` Eric Sunshine
  0 siblings, 0 replies; 154+ messages in thread
From: Eric Sunshine @ 2021-02-15  3:16 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: Git List, Junio C Hamano, Johannes Sixt, Jeff King,
	Jonathan Nieder, Philippe Blain, Adam Spiers

On Sun, Feb 14, 2021 at 7:56 PM Ævar Arnfjörð Bjarmason
<avarab@gmail.com> wrote:
> Add the missing documentation for "diff.<driver>.funcname" and test
> for how it and "diff.<driver>.xfuncname" interact.
> [...]
> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
> diff --git a/Documentation/config/diff.txt b/Documentation/config/diff.txt
> @@ -153,10 +153,22 @@ diff.<driver>.command::
> +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

This would be easier to understand: s/, with/. 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.

Extra blank line before this paragraph?

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

* Re: [PATCH 14/20] gitattributes doc: reword discussion of built-in userdiff patterns
  2021-02-15  0:52       ` [PATCH 14/20] gitattributes doc: reword discussion of built-in userdiff patterns Ævar Arnfjörð Bjarmason
@ 2021-02-15  3:26         ` Eric Sunshine
  0 siblings, 0 replies; 154+ messages in thread
From: Eric Sunshine @ 2021-02-15  3:26 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: Git List, Junio C Hamano, Johannes Sixt, Jeff King,
	Jonathan Nieder, Philippe Blain, Adam Spiers

On Sun, Feb 14, 2021 at 7:56 PM Ævar Arnfjörð Bjarmason
<avarab@gmail.com> wrote:
> 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.
>
> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
> diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
> @@ -794,11 +794,17 @@ backslashes; the pattern above picks a line that begins with a
> -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 the "diff.tex.xfuncname"
> +discussed above in your configuration file, but if present it'll
> +override the built-in pattern.

The literal "diff.tex.xfuncname" now feels disconnected from what is
being said, especially as it is preceded by a reference to generic
"built-in patterns". Perhaps it would make sense to generalize it a
bit to `diff.<lang>.xfuncname`. For instance:

    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.

> +You still need to enable built-in patterns with the the attribute
> +mechanism, via `.gitattributes`).

s/the the/the/

Although you're simply re-using the existing text, it also now feels
disconnected. Perhaps:

    Nevertheless, you need to enable built-in patterns via
    `.gitattributes` for the pattern to take effect.

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

* Re: [PATCH 15/20] gitattributes doc: document multi-line userdiff patterns
  2021-02-15  0:52       ` [PATCH 15/20] gitattributes doc: document multi-line " Ævar Arnfjörð Bjarmason
  2021-02-15  3:16         ` Chris Torek
@ 2021-02-15  3:36         ` Eric Sunshine
  1 sibling, 0 replies; 154+ messages in thread
From: Eric Sunshine @ 2021-02-15  3:36 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: Git List, Junio C Hamano, Johannes Sixt, Jeff King,
	Jonathan Nieder, Philippe Blain, Adam Spiers

On Sun, Feb 14, 2021 at 7:56 PM Ævar Arnfjörð Bjarmason
<avarab@gmail.com> wrote:
> Document the multi-line userdiff patterns and how their matching and
> the negation syntax works.
> [...]
> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
> diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
> @@ -794,6 +794,23 @@ backslashes; the pattern above picks a line that begins with a
> +Multiple patterns can be supplied by seperating them with

s/seperating/separating/

> +newlines. They will be matched one at a time and are compiled as
> +separate patterns, and thus the first capture in each such pattern is
> +`$1`, see further discussion of captures below.

I found the wording "separating them with newlines" ambiguous. I
couldn't figure out if that meant that there must be a blank line
between patterns. Would it be more accurate to say merely that the
patterns must be listed one per line?

> +Patterns that begin with "!" are negated (to match a literal "!" at
> +the start of a line use e.g. "[!]"). A matching negated pattern will
> +cause the matching line to be skipped. Use it to blacklist otherwise
> +matching non-negated patterns. The last pattern must not be negated,
> +we'll error out if that's the case.

The parenthesized comment makes it difficult to follow the discussion.
Moving the comment to the end of the paragraph would make it easier to
grok:

    Patterns that begin with "!" are negated. A matching...
    ...error out if that's the case. To match a literal "!" at
    the start of a line, use "[!]".

I think, also, you want s/matching line/matched line/.

Chris's comma-splice comment also seems applicable for the last
sentence in this paragraph.

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

* [PATCH v2 00/27] userdiff: refactor + test + doc + misc improvements
  2021-02-15  0:52       ` [PATCH 00/20] userdiff: refactor + test + doc + misc improvements Ævar Arnfjörð Bjarmason
@ 2021-02-15 15:44         ` Ævar Arnfjörð Bjarmason
  2021-02-15 15:50           ` [PATCH 0/2] diff: do not display hunk context under -W Ævar Arnfjörð Bjarmason
                             ` (40 more replies)
  2021-02-15 15:44         ` [PATCH v2 01/27] userdiff: refactor away the parse_bool() function Ævar Arnfjörð Bjarmason
                           ` (26 subsequent siblings)
  27 siblings, 41 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15 15:44 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers, Eric Sunshine, Chris Torek,
	Ævar Arnfjörð Bjarmason

Incorporates all the feedback on v2 and more, see the range-diff
below.

Ævar Arnfjörð Bjarmason (27):
  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
  userdiff tests: match full hunk headers
  blame tests: don't rely on t/t4018/ directory
  blame tests: simplify userdiff driver test
  userdiff tests: rewrite hunk header test infrastructure
  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 + docs: document & test "diff.<driver>.x?funcname"
  gitattributes doc: reword discussion of built-in userdiff patterns
  gitattributes doc: document multi-line userdiff patterns
  userdiff tests: remove "funcname" from custom3 test
  userdiff tests: factor out test_diff_funcname() logic
  userdiff tests: test hunk headers on accumulated files
  userdiff tests: test hunk header selection with -U0
  userdiff tests: assert empty hunk header context on -U<large>
  userdiff: match "package" in diff=golang
  userdiff tests: add basic test for ada
  userdiff tests: add basic test for ruby

 Documentation/config/diff.txt              |  11 +
 Documentation/gitattributes.txt            |  41 +++-
 Makefile                                   |   1 +
 t/annotate-tests.sh                        |  34 +--
 t/helper/test-tool.c                       |   1 +
 t/helper/test-tool.h                       |   1 +
 t/helper/test-userdiff.c                   |  30 +++
 t/t4018-diff-funcname.sh                   | 163 ++++++++------
 t/t4018/README                             |  18 --
 t/t4018/ada.sh                             |  37 ++++
 t/t4018/bash-arithmetic-function           |   4 -
 t/t4018/bash-bashism-style-compact         |   6 -
 t/t4018/bash-bashism-style-function        |   4 -
 t/t4018/bash-bashism-style-whitespace      |   4 -
 t/t4018/bash-conditional-function          |   4 -
 t/t4018/bash-missing-parentheses           |   6 -
 t/t4018/bash-mixed-style-compact           |   4 -
 t/t4018/bash-mixed-style-function          |   4 -
 t/t4018/bash-nested-functions              |   6 -
 t/t4018/bash-other-characters              |   4 -
 t/t4018/bash-posix-style-compact           |   4 -
 t/t4018/bash-posix-style-function          |   4 -
 t/t4018/bash-posix-style-whitespace        |   4 -
 t/t4018/bash-subshell-function             |   4 -
 t/t4018/bash-trailing-comment              |   4 -
 t/t4018/bash.sh                            | 160 ++++++++++++++
 t/t4018/cpp-c++-function                   |   4 -
 t/t4018/cpp-class-constructor              |   4 -
 t/t4018/cpp-class-constructor-mem-init     |   5 -
 t/t4018/cpp-class-definition               |   4 -
 t/t4018/cpp-class-definition-derived       |   5 -
 t/t4018/cpp-class-destructor               |   4 -
 t/t4018/cpp-function-returning-global-type |   4 -
 t/t4018/cpp-function-returning-nested      |   5 -
 t/t4018/cpp-function-returning-pointer     |   4 -
 t/t4018/cpp-function-returning-reference   |   4 -
 t/t4018/cpp-gnu-style-function             |   5 -
 t/t4018/cpp-namespace-definition           |   4 -
 t/t4018/cpp-operator-definition            |   4 -
 t/t4018/cpp-skip-access-specifiers         |   8 -
 t/t4018/cpp-skip-comment-block             |   9 -
 t/t4018/cpp-skip-labels                    |   8 -
 t/t4018/cpp-struct-definition              |   9 -
 t/t4018/cpp-struct-single-line             |   7 -
 t/t4018/cpp-template-function-definition   |   4 -
 t/t4018/cpp-union-definition               |   4 -
 t/t4018/cpp-void-c-function                |   4 -
 t/t4018/cpp.sh                             | 240 +++++++++++++++++++++
 t/t4018/css-attribute-value-selector       |   4 -
 t/t4018/css-block-level-@-statements       |  10 -
 t/t4018/css-brace-in-col-1                 |   5 -
 t/t4018/css-class-selector                 |   4 -
 t/t4018/css-colon-eol                      |   4 -
 t/t4018/css-colon-selector                 |   5 -
 t/t4018/css-common                         |   4 -
 t/t4018/css-id-selector                    |   4 -
 t/t4018/css-long-selector-list             |   6 -
 t/t4018/css-prop-sans-indent               |   5 -
 t/t4018/css-root-selector                  |   4 -
 t/t4018/css-short-selector-list            |   4 -
 t/t4018/css-trailing-space                 |   5 -
 t/t4018/css.sh                             | 146 +++++++++++++
 t/t4018/custom.sh                          | 163 ++++++++++++++
 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                         |   9 -
 t/t4018/dts-node-unitless                  |   8 -
 t/t4018/dts-nodes                          |   8 -
 t/t4018/dts-nodes-boolean-prop             |   9 -
 t/t4018/dts-nodes-comment1                 |   8 -
 t/t4018/dts-nodes-comment2                 |   8 -
 t/t4018/dts-nodes-multiline-prop           |  13 --
 t/t4018/dts-reference                      |   9 -
 t/t4018/dts-root                           |   5 -
 t/t4018/dts-root-comment                   |   8 -
 t/t4018/dts.sh                             | 149 +++++++++++++
 t/t4018/elixir-do-not-pick-end             |   5 -
 t/t4018/elixir-ex-unit-test                |   6 -
 t/t4018/elixir-function                    |   5 -
 t/t4018/elixir-macro                       |   5 -
 t/t4018/elixir-module                      |   9 -
 t/t4018/elixir-module-func                 |   8 -
 t/t4018/elixir-nested-module               |   9 -
 t/t4018/elixir-private-function            |   5 -
 t/t4018/elixir-protocol                    |   6 -
 t/t4018/elixir-protocol-implementation     |   5 -
 t/t4018/elixir.sh                          | 127 +++++++++++
 t/t4018/fortran-block-data                 |   5 -
 t/t4018/fortran-comment                    |  13 --
 t/t4018/fortran-comment-keyword            |  14 --
 t/t4018/fortran-comment-legacy             |  13 --
 t/t4018/fortran-comment-legacy-star        |  13 --
 t/t4018/fortran-external-function          |   9 -
 t/t4018/fortran-external-subroutine        |   5 -
 t/t4018/fortran-module                     |   5 -
 t/t4018/fortran-module-procedure           |  13 --
 t/t4018/fortran-program                    |   5 -
 t/t4018/fortran.sh                         | 159 ++++++++++++++
 t/t4018/fountain-scene                     |   4 -
 t/t4018/fountain.sh                        |  14 ++
 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/golang.sh                          |  69 ++++++
 t/t4018/java-class-member-function         |   8 -
 t/t4018/java.sh                            |  18 ++
 t/t4018/markdown-heading-indented          |   6 -
 t/t4018/markdown-heading-non-headings      |  17 --
 t/t4018/markdown.sh                        |  39 ++++
 t/t4018/matlab-class-definition            |   5 -
 t/t4018/matlab-function                    |   4 -
 t/t4018/matlab-octave-section-1            |   3 -
 t/t4018/matlab-octave-section-2            |   3 -
 t/t4018/matlab-section                     |   3 -
 t/t4018/matlab.sh                          |  55 +++++
 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                            |  94 ++++++++
 t/t4018/php-abstract-class                 |   4 -
 t/t4018/php-abstract-method                |   7 -
 t/t4018/php-class                          |   4 -
 t/t4018/php-final-class                    |   4 -
 t/t4018/php-final-method                   |   7 -
 t/t4018/php-function                       |   4 -
 t/t4018/php-interface                      |   4 -
 t/t4018/php-method                         |   7 -
 t/t4018/php-trait                          |   7 -
 t/t4018/php.sh                             | 106 +++++++++
 t/t4018/python-async-def                   |   4 -
 t/t4018/python-class                       |   4 -
 t/t4018/python-def                         |   4 -
 t/t4018/python-indented-async-def          |   7 -
 t/t4018/python-indented-class              |   5 -
 t/t4018/python-indented-def                |   7 -
 t/t4018/python.sh                          |  71 ++++++
 t/t4018/ruby.sh                            |  58 +++++
 t/t4018/rust-fn                            |   5 -
 t/t4018/rust-impl                          |   5 -
 t/t4018/rust-macro-rules                   |   6 -
 t/t4018/rust-struct                        |   5 -
 t/t4018/rust-trait                         |   5 -
 t/t4018/rust.sh                            |  60 ++++++
 userdiff.c                                 | 180 ++++++++++------
 userdiff.h                                 |  15 ++
 150 files changed, 2083 insertions(+), 928 deletions(-)
 create mode 100644 t/helper/test-userdiff.c
 delete mode 100644 t/t4018/README
 create mode 100755 t/t4018/ada.sh
 delete mode 100644 t/t4018/bash-arithmetic-function
 delete mode 100644 t/t4018/bash-bashism-style-compact
 delete mode 100644 t/t4018/bash-bashism-style-function
 delete mode 100644 t/t4018/bash-bashism-style-whitespace
 delete mode 100644 t/t4018/bash-conditional-function
 delete mode 100644 t/t4018/bash-missing-parentheses
 delete mode 100644 t/t4018/bash-mixed-style-compact
 delete mode 100644 t/t4018/bash-mixed-style-function
 delete mode 100644 t/t4018/bash-nested-functions
 delete mode 100644 t/t4018/bash-other-characters
 delete mode 100644 t/t4018/bash-posix-style-compact
 delete mode 100644 t/t4018/bash-posix-style-function
 delete mode 100644 t/t4018/bash-posix-style-whitespace
 delete mode 100644 t/t4018/bash-subshell-function
 delete mode 100644 t/t4018/bash-trailing-comment
 create mode 100755 t/t4018/bash.sh
 delete mode 100644 t/t4018/cpp-c++-function
 delete mode 100644 t/t4018/cpp-class-constructor
 delete mode 100644 t/t4018/cpp-class-constructor-mem-init
 delete mode 100644 t/t4018/cpp-class-definition
 delete mode 100644 t/t4018/cpp-class-definition-derived
 delete mode 100644 t/t4018/cpp-class-destructor
 delete mode 100644 t/t4018/cpp-function-returning-global-type
 delete mode 100644 t/t4018/cpp-function-returning-nested
 delete mode 100644 t/t4018/cpp-function-returning-pointer
 delete mode 100644 t/t4018/cpp-function-returning-reference
 delete mode 100644 t/t4018/cpp-gnu-style-function
 delete mode 100644 t/t4018/cpp-namespace-definition
 delete mode 100644 t/t4018/cpp-operator-definition
 delete mode 100644 t/t4018/cpp-skip-access-specifiers
 delete mode 100644 t/t4018/cpp-skip-comment-block
 delete mode 100644 t/t4018/cpp-skip-labels
 delete mode 100644 t/t4018/cpp-struct-definition
 delete mode 100644 t/t4018/cpp-struct-single-line
 delete mode 100644 t/t4018/cpp-template-function-definition
 delete mode 100644 t/t4018/cpp-union-definition
 delete mode 100644 t/t4018/cpp-void-c-function
 create mode 100755 t/t4018/cpp.sh
 delete mode 100644 t/t4018/css-attribute-value-selector
 delete mode 100644 t/t4018/css-block-level-@-statements
 delete mode 100644 t/t4018/css-brace-in-col-1
 delete mode 100644 t/t4018/css-class-selector
 delete mode 100644 t/t4018/css-colon-eol
 delete mode 100644 t/t4018/css-colon-selector
 delete mode 100644 t/t4018/css-common
 delete mode 100644 t/t4018/css-id-selector
 delete mode 100644 t/t4018/css-long-selector-list
 delete mode 100644 t/t4018/css-prop-sans-indent
 delete mode 100644 t/t4018/css-root-selector
 delete mode 100644 t/t4018/css-short-selector-list
 delete mode 100644 t/t4018/css-trailing-space
 create mode 100755 t/t4018/css.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
 delete mode 100644 t/t4018/dts-labels
 delete mode 100644 t/t4018/dts-node-unitless
 delete mode 100644 t/t4018/dts-nodes
 delete mode 100644 t/t4018/dts-nodes-boolean-prop
 delete mode 100644 t/t4018/dts-nodes-comment1
 delete mode 100644 t/t4018/dts-nodes-comment2
 delete mode 100644 t/t4018/dts-nodes-multiline-prop
 delete mode 100644 t/t4018/dts-reference
 delete mode 100644 t/t4018/dts-root
 delete mode 100644 t/t4018/dts-root-comment
 create mode 100755 t/t4018/dts.sh
 delete mode 100644 t/t4018/elixir-do-not-pick-end
 delete mode 100644 t/t4018/elixir-ex-unit-test
 delete mode 100644 t/t4018/elixir-function
 delete mode 100644 t/t4018/elixir-macro
 delete mode 100644 t/t4018/elixir-module
 delete mode 100644 t/t4018/elixir-module-func
 delete mode 100644 t/t4018/elixir-nested-module
 delete mode 100644 t/t4018/elixir-private-function
 delete mode 100644 t/t4018/elixir-protocol
 delete mode 100644 t/t4018/elixir-protocol-implementation
 create mode 100755 t/t4018/elixir.sh
 delete mode 100644 t/t4018/fortran-block-data
 delete mode 100644 t/t4018/fortran-comment
 delete mode 100644 t/t4018/fortran-comment-keyword
 delete mode 100644 t/t4018/fortran-comment-legacy
 delete mode 100644 t/t4018/fortran-comment-legacy-star
 delete mode 100644 t/t4018/fortran-external-function
 delete mode 100644 t/t4018/fortran-external-subroutine
 delete mode 100644 t/t4018/fortran-module
 delete mode 100644 t/t4018/fortran-module-procedure
 delete mode 100644 t/t4018/fortran-program
 create mode 100755 t/t4018/fortran.sh
 delete mode 100644 t/t4018/fountain-scene
 create mode 100755 t/t4018/fountain.sh
 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
 create mode 100755 t/t4018/golang.sh
 delete mode 100644 t/t4018/java-class-member-function
 create mode 100755 t/t4018/java.sh
 delete mode 100644 t/t4018/markdown-heading-indented
 delete mode 100644 t/t4018/markdown-heading-non-headings
 create mode 100755 t/t4018/markdown.sh
 delete mode 100644 t/t4018/matlab-class-definition
 delete mode 100644 t/t4018/matlab-function
 delete mode 100644 t/t4018/matlab-octave-section-1
 delete mode 100644 t/t4018/matlab-octave-section-2
 delete mode 100644 t/t4018/matlab-section
 create mode 100755 t/t4018/matlab.sh
 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
 delete mode 100644 t/t4018/php-abstract-class
 delete mode 100644 t/t4018/php-abstract-method
 delete mode 100644 t/t4018/php-class
 delete mode 100644 t/t4018/php-final-class
 delete mode 100644 t/t4018/php-final-method
 delete mode 100644 t/t4018/php-function
 delete mode 100644 t/t4018/php-interface
 delete mode 100644 t/t4018/php-method
 delete mode 100644 t/t4018/php-trait
 create mode 100755 t/t4018/php.sh
 delete mode 100644 t/t4018/python-async-def
 delete mode 100644 t/t4018/python-class
 delete mode 100644 t/t4018/python-def
 delete mode 100644 t/t4018/python-indented-async-def
 delete mode 100644 t/t4018/python-indented-class
 delete mode 100644 t/t4018/python-indented-def
 create mode 100755 t/t4018/python.sh
 create mode 100755 t/t4018/ruby.sh
 delete mode 100644 t/t4018/rust-fn
 delete mode 100644 t/t4018/rust-impl
 delete mode 100644 t/t4018/rust-macro-rules
 delete mode 100644 t/t4018/rust-struct
 delete mode 100644 t/t4018/rust-trait
 create mode 100755 t/t4018/rust.sh

Range-diff:
 -:  ---------- >  1:  305fc646d0 userdiff: refactor away the parse_bool() function
 1:  7dbe7d638e !  2:  989438c46a userdiff: re-order builtin drivers in alphabetical order
    @@ Metadata
     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## Commit message ##
    -    userdiff: re-order builtin drivers in alphabetical order
    +    userdiff style: re-order drivers in alphabetical order
     
         Address some old code smell and move around the built-in userdiff
    -    definitions so they're both in alphabetical order, and now in the same
    +    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
 -:  ---------- >  3:  4c48e5532c userdiff style: declare patterns with consistent style
 -:  ---------- >  4:  f41fa5b316 userdiff style: normalize pascal regex declaration
 2:  69532bfb68 =  5:  0875d5205c userdiff: add and use for_each_userdiff_driver()
 3:  6ad7a5f608 =  6:  638247d04d userdiff tests: explicitly test "default" pattern
 4:  48b8c39380 !  7:  219043a488 userdiff tests: list builtin drivers via test-tool
    @@ t/helper/test-userdiff.c (new)
     +#include "cache.h"
     +#include "userdiff.h"
     +
    -+static int userdiff_list_builtin_drivers_cb(struct userdiff_driver *driver,
    -+					    enum userdiff_driver_type type,
    -+					    void *priv)
    ++static int driver_cb(struct userdiff_driver *driver,
    ++		     enum userdiff_driver_type type, void *priv)
     +{
     +	puts(driver->name);
     +	return 0;
    @@ t/helper/test-userdiff.c (new)
     +
     +static int list_what(enum userdiff_driver_type type)
     +{
    -+	return for_each_userdiff_driver(userdiff_list_builtin_drivers_cb, type,
    -+					NULL);
    ++	return for_each_userdiff_driver(driver_cb, type, NULL);
     +}
     +
     +int cmd__userdiff(int argc, const char **argv)
    @@ t/t4018-diff-funcname.sh: test_description='Test custom diff function name patte
      . ./test-lib.sh
      
      test_expect_success 'setup' '
    -+	test-tool userdiff list-builtin-drivers >builtin-drivers &&
    -+	test_file_not_empty builtin-drivers &&
    -+	builtin_drivers=$(cat builtin-drivers) &&
    ++	builtin_drivers=$(test-tool userdiff list-builtin-drivers) &&
    ++	test -n "$builtin_drivers" &&
     +
      	# a non-trivial custom pattern
      	git config diff.custom1.funcname "!static
 5:  3073d07409 =  8:  eb66160aac userdiff: remove support for "broken" tests
 6:  fd93d18351 !  9:  c6c54039e2 userdiff tests: match full hunk headers
    @@ Commit message
         towards even better test coverage. I'm structuring these tests in such
         a way as to benefit from the diff.colorMove detection.
     
    +    The "sed -n -e" here was originally a single 's/^.*@@\( \|$\)//p'
    +    pattern, but the '\( \|$\)' part had portability issues on OSX and
    +    AIX.
    +
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## t/t4018-diff-funcname.sh ##
    @@ t/t4018-diff-funcname.sh: test_expect_success 'setup hunk header tests' '
     -		git diff -U1 $i >actual &&
     -		grep '@@ .* @@.*RIGHT' actual
     +		git diff -U1 $i >diff &&
    -+		sed -n -e 's/^.*@@\( \|$\)//p' <diff >ctx &&
    ++		sed -n -e 's/^.*@@$//p' -e 's/^.*@@ //p' <diff >ctx &&
     +		test_cmp $i.ctx ctx
      	"
      done
    @@ t/t4018/README
     -of equal signs.
     +The text that must appear in the hunk header must contains the word
     +"RIGHT" by convention. The "LANG-whatever.ctx" file contains what we
    -+expect to appear in the hunk header. We munged the start of the line
    -+to "@@ [...] @@" for ease of not having to hardcode the line numbers
    -+and offsets.
    ++expect to appear in the hunk header. We munged away the starting "@@
    ++[...] @@" part of the line for ease of not having to hardcode the line
    ++numbers and offsets.
     
      ## t/t4018/README.ctx (new) ##
     @@
 8:  034ab9f85b ! 10:  1c6ddf96f6 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, just moving the relevant test
    -    file here inline is the easiest solution, and I think also the most
    -    readable.
    +    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.
     
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
 -:  ---------- > 11:  8a883d8799 blame tests: simplify userdiff driver test
 7:  4f8df97207 ! 12:  e56a7a6b5f userdiff tests: rewrite hunk header test infrastructure
    @@ t/t4018-diff-funcname.sh: test_expect_success 'last regexp must not be negated'
     +
     +	test_expect_success "$desc" '
     +		git diff -U1 "$what" >diff &&
    -+		sed -n -e "s/^.*@@\( \|$\)//p" <diff >actual &&
    ++		sed -n -e "s/^.*@@$//p" -e "s/^.*@@ //p" <diff >actual &&
     +		test_cmp expected actual
     +	'
     +}
    @@ t/t4018-diff-funcname.sh: test_expect_success 'last regexp must not be negated'
      do
     -	test_expect_success "hunk header: $i" "
     -		git diff -U1 $i >diff &&
    --		sed -n -e 's/^.*@@\( \|$\)//p' <diff >ctx &&
    +-		sed -n -e 's/^.*@@$//p' -e 's/^.*@@ //p' <diff >ctx &&
     -		test_cmp $i.ctx ctx
     -	"
     +	test="$TEST_DIRECTORY/t4018/$what.sh"
    @@ t/t4018/README (deleted)
     -
     -The text that must appear in the hunk header must contains the word
     -"RIGHT" by convention. The "LANG-whatever.ctx" file contains what we
    --expect to appear in the hunk header. We munged the start of the line
    --to "@@ [...] @@" for ease of not having to hardcode the line numbers
    --and offsets.
    +-expect to appear in the hunk header. We munged away the starting "@@
    +-[...] @@" part of the line for ease of not having to hardcode the line
    +-numbers and offsets.
     
      ## t/t4018/README.ctx (deleted) ##
     @@
 -:  ---------- > 13:  84d20a7cd0 userdiff tests: do config teardown in test_diff_funcname()
 9:  e48ad2b57f ! 14:  70fc9fa565 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' '
    - 	test_file_not_empty builtin-drivers &&
    - 	builtin_drivers=$(cat builtin-drivers) &&
    + 	builtin_drivers=$(test-tool userdiff list-builtin-drivers) &&
    + 	test -n "$builtin_drivers" &&
      
     -	# a non-trivial custom pattern
     -	git config diff.custom1.funcname "!static
    @@ t/t4018/custom.sh (new)
     +	}
     +}
     +EOF_TEST
    -+
    -+test_expect_success 'custom: teardown' '
    -+	test_unconfig diff.custom.funcname &&
    -+	test_unconfig diff.custom.xfuncname
    -+'
     
      ## t/t4018/custom1.sh (deleted) ##
     @@
10:  6a4224b512 = 15:  8539d6d464 userdiff tests: remove hack for "RIGHT" token
 -:  ---------- > 16:  121e5d6dfa userdiff tests: do not do compile tests on "custom" pattern
12:  9ce7de5698 ! 17:  451b7ae453 userdiff tests + docs: document & test "diff.<driver>.x?funcname"
    @@ Documentation/config/diff.txt: diff.<driver>.command::
      	See linkgit:gitattributes[5] for details.
     ++
     +When provided as `diff.<driver>.funcname` the regular expression is
    -+interpreted as a basic regular expression, with
    ++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
    @@ Documentation/config/diff.txt: diff.<driver>.command::
     
      ## t/t4018/custom.sh ##
     @@ t/t4018/custom.sh: public class Beer
    + 	}
      }
      EOF_TEST
    - 
    -+test_expect_success 'custom; setup config precedence' '
    ++
    ++test_expect_success 'custom: setup config precedence' '
     +	git config diff.custom.funcname "foo" &&
     +	git config diff.custom.xfuncname "bar"
     +'
    @@ t/t4018/custom.sh: public class Beer
     +baz
     +EOF_TEST
     +
    -+test_expect_success 'custom: teardown' '
    -+	test_unconfig diff.custom.funcname &&
    -+	test_unconfig diff.custom.xfuncname
    -+'
    -+
    -+test_expect_success 'custom; setup config precedence' '
    ++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' \
    @@ t/t4018/custom.sh: public class Beer
     +
     +baz
     +EOF_TEST
    -+
    - test_expect_success 'custom: teardown' '
    - 	test_unconfig diff.custom.funcname &&
    - 	test_unconfig diff.custom.xfuncname
13:  3a9f64b747 ! 18:  5a402bb9bf gitattributes doc: reword discussion of built-in userdiff patterns
    @@ Commit message
         "diff.tex.xfuncname" example which now immediately precedes it. This
         will make a follow-up commit smaller.
     
    +    Helped-by: Eric Sunshine <sunshine@sunshineco.com>
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## Documentation/gitattributes.txt ##
    @@ 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.
     +
    -+For built-in patterns you do not need the "diff.tex.xfuncname"
    -+discussed above in your configuration file, but if present it'll
    -+override the built-in pattern.
    ++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.
     +
    -+You still need to enable built-in patterns with the the attribute
    -+mechanism, via `.gitattributes`).
    ++Nevertheless, you need to enable built-in patterns via .gitattributes`
    ++for the pattern to take effect.
     +
    -+The following built in patterns are available:
    ++The following built-in patterns are available:
      
      - `ada` suitable for source code in the Ada language.
      
14:  a3e8d0bfc1 ! 19:  a3badb1a3e gitattributes doc: document multi-line userdiff patterns
    @@ Documentation/gitattributes.txt: backslashes; the pattern above picks a line tha
      backslash, and zero or more occurrences of `sub` followed by
      `section` followed by open brace, to the end of line.
      
    -+Multiple patterns can be supplied by seperating them with
    -+newlines. They will be matched one at a time and are compiled as
    -+separate patterns, and thus the first capture in each such pattern is
    -+`$1`, see further discussion of captures below.
    +-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.:
     +
    -+Patterns that begin with "!" are negated (to match a literal "!" at
    -+the start of a line use e.g. "[!]"). A matching negated pattern will
    -+cause the matching line to be skipped. Use it to blacklist otherwise
    -+matching non-negated patterns. The last pattern must not be negated,
    -+we'll error out if that's the case.
    ++------------------------
    ++[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
    ++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 pattern contains a `$1` capture it will be used instead of the
     +entire matching line (`$0`) to display the hunk header. This can be
    @@ Documentation/gitattributes.txt: backslashes; the pattern above picks a line tha
     +only display the function name as part of a longer function
     +definition.
     +
    - 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.
    ++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.
     
      ## t/t4018/custom.sh ##
    -@@ t/t4018/custom.sh: test_expect_success 'custom: teardown' '
    - 	test_unconfig diff.custom.funcname &&
    - 	test_unconfig diff.custom.xfuncname
    - '
    +@@ t/t4018/custom.sh: ChangeMe
    + 
    + baz
    + EOF_TEST
     +
    -+test_expect_success 'custom: negation syntax, ! is magic' '
    ++test_expect_success 'custom: setup negation syntax, ! is magic' '
     +	git config diff.custom.xfuncname "!negation
     +line"
     +'
     +
    -+test_diff_funcname 'custom: config precedence' \
    ++test_diff_funcname 'custom: negation syntax, ! is magic' \
     +	8<<\EOF_HUNK 9<<\EOF_TEST
     +line
     +EOF_HUNK
    @@ t/t4018/custom.sh: test_expect_success 'custom: teardown' '
     +baz
     +EOF_TEST
     +
    -+test_expect_success 'custom: negation syntax, use [!] to override ! magic' '
    ++test_expect_success 'custom: setup negation syntax, use [!] to override ! magic' '
     +	git config diff.custom.xfuncname "[!]negation
     +line"
     +'
     +
    -+test_diff_funcname 'custom: config precedence' \
    ++test_diff_funcname 'custom: negation syntax, use [!] to override ! magic' \
     +	8<<\EOF_HUNK 9<<\EOF_TEST
     +!negation
     +EOF_HUNK
    @@ t/t4018/custom.sh: test_expect_success 'custom: teardown' '
     +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
    +
    + ## t/t4018/perl.sh ##
    +@@ t/t4018/perl.sh: sub RIGHT
    + 	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
15:  b83cf4da91 ! 20:  1b46726e85 userdiff tests: remove "funcname" from custom3 test
    @@ t/t4018/custom.sh: public class Beer
      	git config diff.custom.xfuncname "^[ 	]*((public|static).*)$"
      '
      
    -@@ t/t4018/custom.sh: test_diff_funcname 'custom: config precedence' \
    - line
    - EOF_HUNK
    - line
    -+
    - !negation
    - 
    - ChangeMe
16:  f286e42540 ! 21:  9a18506aff userdiff tests: factor out test_diff_funcname() logic
    @@ t/t4018-diff-funcname.sh: test_expect_success 'last regexp must not be negated'
     +
     +last_diff_context_line () {
     +	file=$1
    -+	sed -n -e "s/^.*@@\( \|$\)//p" <$file
    ++	sed -n -e "s/^.*@@$//p" -e "s/^.*@@ //p" <$file
     +}
     +
      test_diff_funcname () {
    @@ t/t4018-diff-funcname.sh: test_diff_funcname () {
      
      	test_expect_success "$desc" '
      		git diff -U1 "$what" >diff &&
    --		sed -n -e "s/^.*@@\( \|$\)//p" <diff >actual &&
    +-		sed -n -e "s/^.*@@$//p" -e "s/^.*@@ //p" <diff >actual &&
     +		last_diff_context_line diff >actual &&
      		test_cmp expected actual
    - 	'
    - }
    + 	' &&
    + 
17:  12635fefd6 ! 22:  24548fb680 userdiff tests: test hunk headers on accumulated files
    @@ t/t4018-diff-funcname.sh: test_diff_funcname () {
      		git diff -U1 "$what" >diff &&
      		last_diff_context_line diff >actual &&
      		test_cmp expected actual
    -+	' &&
    -+
    + 	' &&
    + 
     +	test_expect_success "$desc (accumulated)" '
     +		git diff -U1 "$what".acc >diff &&
     +		last_diff_context_line diff >actual.lines &&
     +		tail -n 1 actual.lines >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
     @@ t/t4018-diff-funcname.sh: do
      		echo "$what" >arg.what
      	' &&
18:  3eef2896ea = 23:  48f00a59d5 userdiff tests: test hunk header selection with -U0
19:  3061fac48c ! 24:  05a01990c9 userdiff tests: assert empty hunk header context on -U<large>
    @@ Commit message
     
      ## t/t4018-diff-funcname.sh ##
     @@ t/t4018-diff-funcname.sh: test_diff_funcname () {
    - 		tail -n 1 actual.lines >actual &&
      		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 &&
     +		echo >blank &&
     +		test_cmp blank actual
    -+	'
    - }
    - 
    - for what in $diffpatterns
    ++	' &&
    ++
    + 	test_expect_success "teardown: $desc" '
    + 		# In case any custom config was set immediately before
    + 		# the test itself in the test file
11:  0901623546 ! 25:  3d2f42d704 userdiff: match "package" in diff=golang
    @@ t/t4018/golang.sh
      func (t *Test) RIGHT(a Type) (Type, error) {
     
      ## userdiff.c ##
    -@@ userdiff.c: IPATTERN("fortran",
    - IPATTERN("fountain", "^((\\.[^.]|(int|ext|est|int\\.?/ext|i/e)[. ]).*)$",
    +@@ userdiff.c: IPATTERN("fountain",
    + 	 /* -- */
      	 "[^ \t-]+"),
      PATTERNS("golang",
     +	 /* Packages */
 -:  ---------- > 26:  b2e16ade06 userdiff tests: add basic test for ada
 -:  ---------- > 27:  826b6f4d6a userdiff tests: add basic test for ruby
-- 
2.30.0.284.gd98b1dd5eaa7


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

* [PATCH v2 01/27] userdiff: refactor away the parse_bool() function
  2021-02-15  0:52       ` [PATCH 00/20] userdiff: refactor + test + doc + misc improvements Ævar Arnfjörð Bjarmason
  2021-02-15 15:44         ` [PATCH v2 00/27] " Ævar Arnfjörð Bjarmason
@ 2021-02-15 15:44         ` Ævar Arnfjörð Bjarmason
  2021-02-15 15:44         ` [PATCH v2 02/27] userdiff style: re-order drivers in alphabetical order Ævar Arnfjörð Bjarmason
                           ` (25 subsequent siblings)
  27 siblings, 0 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15 15:44 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers, Eric Sunshine, Chris Torek,
	Ævar Arnfjörð Bjarmason

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 <avarab@gmail.com>
---
 userdiff.c | 18 ++++++------------
 1 file changed, 6 insertions(+), 12 deletions(-)

diff --git a/userdiff.c b/userdiff.c
index 3f81a2261c..c147bcbb17 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


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

* [PATCH v2 02/27] userdiff style: re-order drivers in alphabetical order
  2021-02-15  0:52       ` [PATCH 00/20] userdiff: refactor + test + doc + misc improvements Ævar Arnfjörð Bjarmason
  2021-02-15 15:44         ` [PATCH v2 00/27] " Ævar Arnfjörð Bjarmason
  2021-02-15 15:44         ` [PATCH v2 01/27] userdiff: refactor away the parse_bool() function Ævar Arnfjörð Bjarmason
@ 2021-02-15 15:44         ` Ævar Arnfjörð Bjarmason
  2021-02-15 15:44         ` [PATCH v2 03/27] userdiff style: declare patterns with consistent style Ævar Arnfjörð Bjarmason
                           ` (24 subsequent siblings)
  27 siblings, 0 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15 15:44 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers, Eric Sunshine, Chris Torek,
	Ævar Arnfjörð Bjarmason

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 <avarab@gmail.com>
---
 userdiff.c | 76 +++++++++++++++++++++++++++---------------------------
 1 file changed, 38 insertions(+), 38 deletions(-)

diff --git a/userdiff.c b/userdiff.c
index c147bcbb17..c92cbcc054 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


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

* [PATCH v2 03/27] userdiff style: declare patterns with consistent style
  2021-02-15  0:52       ` [PATCH 00/20] userdiff: refactor + test + doc + misc improvements Ævar Arnfjörð Bjarmason
                           ` (2 preceding siblings ...)
  2021-02-15 15:44         ` [PATCH v2 02/27] userdiff style: re-order drivers in alphabetical order Ævar Arnfjörð Bjarmason
@ 2021-02-15 15:44         ` Ævar Arnfjörð Bjarmason
  2021-02-15 15:44         ` [PATCH v2 04/27] userdiff style: normalize pascal regex declaration Ævar Arnfjörð Bjarmason
                           ` (23 subsequent siblings)
  27 siblings, 0 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15 15:44 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers, Eric Sunshine, Chris Torek,
	Ævar Arnfjörð Bjarmason

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 <avarab@gmail.com>
---
 userdiff.c | 20 +++++++++++++++-----
 1 file changed, 15 insertions(+), 5 deletions(-)

diff --git a/userdiff.c b/userdiff.c
index c92cbcc054..c7aaf7094f 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


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

* [PATCH v2 04/27] userdiff style: normalize pascal regex declaration
  2021-02-15  0:52       ` [PATCH 00/20] userdiff: refactor + test + doc + misc improvements Ævar Arnfjörð Bjarmason
                           ` (3 preceding siblings ...)
  2021-02-15 15:44         ` [PATCH v2 03/27] userdiff style: declare patterns with consistent style Ævar Arnfjörð Bjarmason
@ 2021-02-15 15:44         ` Ævar Arnfjörð Bjarmason
  2021-02-15 15:44         ` [PATCH v2 05/27] userdiff: add and use for_each_userdiff_driver() Ævar Arnfjörð Bjarmason
                           ` (22 subsequent siblings)
  27 siblings, 0 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15 15:44 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers, Eric Sunshine, Chris Torek,
	Ævar Arnfjörð Bjarmason

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 <avarab@gmail.com>
---
 userdiff.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/userdiff.c b/userdiff.c
index c7aaf7094f..10a02d3620 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


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

* [PATCH v2 05/27] userdiff: add and use for_each_userdiff_driver()
  2021-02-15  0:52       ` [PATCH 00/20] userdiff: refactor + test + doc + misc improvements Ævar Arnfjörð Bjarmason
                           ` (4 preceding siblings ...)
  2021-02-15 15:44         ` [PATCH v2 04/27] userdiff style: normalize pascal regex declaration Ævar Arnfjörð Bjarmason
@ 2021-02-15 15:44         ` Ævar Arnfjörð Bjarmason
  2021-02-15 15:44         ` [PATCH v2 06/27] userdiff tests: explicitly test "default" pattern Ævar Arnfjörð Bjarmason
                           ` (21 subsequent siblings)
  27 siblings, 0 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15 15:44 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers, Eric Sunshine, Chris Torek,
	Ævar Arnfjörð Bjarmason

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 <avarab@gmail.com>
---
 userdiff.c | 61 +++++++++++++++++++++++++++++++++++++++++++-----------
 userdiff.h | 15 ++++++++++++++
 2 files changed, 64 insertions(+), 12 deletions(-)

diff --git a/userdiff.c b/userdiff.c
index 10a02d3620..55f4f769bd 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 203057e13e..fe14014a77 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


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

* [PATCH v2 06/27] userdiff tests: explicitly test "default" pattern
  2021-02-15  0:52       ` [PATCH 00/20] userdiff: refactor + test + doc + misc improvements Ævar Arnfjörð Bjarmason
                           ` (5 preceding siblings ...)
  2021-02-15 15:44         ` [PATCH v2 05/27] userdiff: add and use for_each_userdiff_driver() Ævar Arnfjörð Bjarmason
@ 2021-02-15 15:44         ` Ævar Arnfjörð Bjarmason
  2021-02-15 15:44         ` [PATCH v2 07/27] userdiff tests: list builtin drivers via test-tool Ævar Arnfjörð Bjarmason
                           ` (20 subsequent siblings)
  27 siblings, 0 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15 15:44 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers, Eric Sunshine, Chris Torek,
	Ævar Arnfjörð Bjarmason

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 <avarab@gmail.com>
---
 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 9675bc17db..cefe329aea 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


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

* [PATCH v2 07/27] userdiff tests: list builtin drivers via test-tool
  2021-02-15  0:52       ` [PATCH 00/20] userdiff: refactor + test + doc + misc improvements Ævar Arnfjörð Bjarmason
                           ` (6 preceding siblings ...)
  2021-02-15 15:44         ` [PATCH v2 06/27] userdiff tests: explicitly test "default" pattern Ævar Arnfjörð Bjarmason
@ 2021-02-15 15:44         ` Ævar Arnfjörð Bjarmason
  2021-02-15 15:44         ` [PATCH v2 08/27] userdiff: remove support for "broken" tests Ævar Arnfjörð Bjarmason
                           ` (19 subsequent siblings)
  27 siblings, 0 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15 15:44 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers, Eric Sunshine, Chris Torek,
	Ævar Arnfjörð Bjarmason

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 <avarab@gmail.com>
---
 Makefile                 |  1 +
 t/helper/test-tool.c     |  1 +
 t/helper/test-tool.h     |  1 +
 t/helper/test-userdiff.c | 30 ++++++++++++++++++++++++++++++
 t/t4018-diff-funcname.sh | 28 ++++------------------------
 5 files changed, 37 insertions(+), 24 deletions(-)
 create mode 100644 t/helper/test-userdiff.c

diff --git a/Makefile b/Makefile
index 5a239cac20..710a0deaed 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 f97cd9f48a..dcb05ca6e5 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 28072c0ad5..589f2e8ac6 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 0000000000..1e17aeb265
--- /dev/null
+++ b/t/helper/test-userdiff.c
@@ -0,0 +1,30 @@
+#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)
+{
+	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 cefe329aea..6faa719536 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -8,6 +8,9 @@ test_description='Test custom diff function name patterns'
 . ./test-lib.sh
 
 test_expect_success 'setup' '
+	builtin_drivers=$(test-tool userdiff list-builtin-drivers) &&
+	test -n "$builtin_drivers" &&
+
 	# a non-trivial custom pattern
 	git config diff.custom1.funcname "!static
 !String
@@ -26,30 +29,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
+	$builtin_drivers
 	custom1
 	custom2
 	custom3
-- 
2.30.0.284.gd98b1dd5eaa7


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

* [PATCH v2 08/27] userdiff: remove support for "broken" tests
  2021-02-15  0:52       ` [PATCH 00/20] userdiff: refactor + test + doc + misc improvements Ævar Arnfjörð Bjarmason
                           ` (7 preceding siblings ...)
  2021-02-15 15:44         ` [PATCH v2 07/27] userdiff tests: list builtin drivers via test-tool Ævar Arnfjörð Bjarmason
@ 2021-02-15 15:44         ` Ævar Arnfjörð Bjarmason
  2021-02-15 15:44         ` [PATCH v2 09/27] userdiff tests: match full hunk headers Ævar Arnfjörð Bjarmason
                           ` (18 subsequent siblings)
  27 siblings, 0 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15 15:44 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers, Eric Sunshine, Chris Torek,
	Ævar Arnfjörð Bjarmason

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 <avarab@gmail.com>
---
 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 6faa719536..5994c5b47a 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -82,13 +82,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 283e01cca1..2d25b2b4fc 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


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

* [PATCH v2 09/27] userdiff tests: match full hunk headers
  2021-02-15  0:52       ` [PATCH 00/20] userdiff: refactor + test + doc + misc improvements Ævar Arnfjörð Bjarmason
                           ` (8 preceding siblings ...)
  2021-02-15 15:44         ` [PATCH v2 08/27] userdiff: remove support for "broken" tests Ævar Arnfjörð Bjarmason
@ 2021-02-15 15:44         ` Ævar Arnfjörð Bjarmason
  2021-02-15 17:13           ` Johannes Sixt
  2021-02-15 15:44         ` [PATCH v2 10/27] blame tests: don't rely on t/t4018/ directory Ævar Arnfjörð Bjarmason
                           ` (17 subsequent siblings)
  27 siblings, 1 reply; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15 15:44 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers, Eric Sunshine, Chris Torek,
	Ævar Arnfjörð Bjarmason

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 adding corresponding *.ctx
files, this has the added advantage that we're doing a "test_cmp", so
when we have failures it's just a non-zero exit code from "grep",
we'll actually have something meaningful in the "-v" output.

As we'll see in a follow-up commit this is an intermediate step
towards even better test coverage. I'm structuring these tests in such
a way as to benefit from the diff.colorMove detection.

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

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 t/t4018-diff-funcname.sh                      |  7 +++---
 t/t4018/README                                | 22 +++++++++----------
 t/t4018/README.ctx                            |  1 +
 t/t4018/bash-arithmetic-function.ctx          |  1 +
 t/t4018/bash-bashism-style-compact.ctx        |  1 +
 t/t4018/bash-bashism-style-function.ctx       |  1 +
 t/t4018/bash-bashism-style-whitespace.ctx     |  1 +
 t/t4018/bash-conditional-function.ctx         |  1 +
 t/t4018/bash-missing-parentheses.ctx          |  1 +
 t/t4018/bash-mixed-style-compact.ctx          |  1 +
 t/t4018/bash-mixed-style-function.ctx         |  1 +
 t/t4018/bash-nested-functions.ctx             |  1 +
 t/t4018/bash-other-characters.ctx             |  1 +
 t/t4018/bash-posix-style-compact.ctx          |  1 +
 t/t4018/bash-posix-style-function.ctx         |  1 +
 t/t4018/bash-posix-style-whitespace.ctx       |  1 +
 t/t4018/bash-subshell-function.ctx            |  1 +
 t/t4018/bash-trailing-comment.ctx             |  1 +
 t/t4018/cpp-c++-function.ctx                  |  1 +
 t/t4018/cpp-class-constructor-mem-init.ctx    |  1 +
 t/t4018/cpp-class-constructor.ctx             |  1 +
 t/t4018/cpp-class-definition-derived.ctx      |  1 +
 t/t4018/cpp-class-definition.ctx              |  1 +
 t/t4018/cpp-class-destructor.ctx              |  1 +
 .../cpp-function-returning-global-type.ctx    |  1 +
 t/t4018/cpp-function-returning-nested.ctx     |  1 +
 t/t4018/cpp-function-returning-pointer.ctx    |  1 +
 t/t4018/cpp-function-returning-reference.ctx  |  1 +
 t/t4018/cpp-gnu-style-function.ctx            |  1 +
 t/t4018/cpp-namespace-definition.ctx          |  1 +
 t/t4018/cpp-operator-definition.ctx           |  1 +
 t/t4018/cpp-skip-access-specifiers.ctx        |  1 +
 t/t4018/cpp-skip-comment-block.ctx            |  1 +
 t/t4018/cpp-skip-labels.ctx                   |  1 +
 t/t4018/cpp-struct-definition.ctx             |  1 +
 t/t4018/cpp-struct-single-line.ctx            |  1 +
 t/t4018/cpp-template-function-definition.ctx  |  1 +
 t/t4018/cpp-union-definition.ctx              |  1 +
 t/t4018/cpp-void-c-function.ctx               |  1 +
 t/t4018/css-attribute-value-selector.ctx      |  1 +
 t/t4018/css-block-level-@-statements.ctx      |  1 +
 t/t4018/css-brace-in-col-1.ctx                |  1 +
 t/t4018/css-class-selector.ctx                |  1 +
 t/t4018/css-colon-eol.ctx                     |  1 +
 t/t4018/css-colon-selector.ctx                |  1 +
 t/t4018/css-common.ctx                        |  1 +
 t/t4018/css-id-selector.ctx                   |  1 +
 t/t4018/css-long-selector-list.ctx            |  1 +
 t/t4018/css-prop-sans-indent.ctx              |  1 +
 t/t4018/css-root-selector.ctx                 |  1 +
 t/t4018/css-short-selector-list.ctx           |  1 +
 t/t4018/css-trailing-space.ctx                |  1 +
 t/t4018/custom1-pattern.ctx                   |  1 +
 t/t4018/custom2-match-to-end-of-line.ctx      |  1 +
 t/t4018/custom3-alternation-in-pattern.ctx    |  1 +
 t/t4018/dts-labels.ctx                        |  1 +
 t/t4018/dts-node-unitless.ctx                 |  1 +
 t/t4018/dts-nodes-boolean-prop.ctx            |  1 +
 t/t4018/dts-nodes-comment1.ctx                |  1 +
 t/t4018/dts-nodes-comment2.ctx                |  1 +
 t/t4018/dts-nodes-multiline-prop.ctx          |  1 +
 t/t4018/dts-nodes.ctx                         |  1 +
 t/t4018/dts-reference.ctx                     |  1 +
 t/t4018/dts-root-comment.ctx                  |  1 +
 t/t4018/dts-root.ctx                          |  1 +
 t/t4018/elixir-do-not-pick-end.ctx            |  1 +
 t/t4018/elixir-ex-unit-test.ctx               |  1 +
 t/t4018/elixir-function.ctx                   |  1 +
 t/t4018/elixir-macro.ctx                      |  1 +
 t/t4018/elixir-module-func.ctx                |  1 +
 t/t4018/elixir-module.ctx                     |  1 +
 t/t4018/elixir-nested-module.ctx              |  1 +
 t/t4018/elixir-private-function.ctx           |  1 +
 t/t4018/elixir-protocol-implementation.ctx    |  1 +
 t/t4018/elixir-protocol.ctx                   |  1 +
 t/t4018/fortran-block-data.ctx                |  1 +
 t/t4018/fortran-comment-keyword.ctx           |  1 +
 t/t4018/fortran-comment-legacy-star.ctx       |  1 +
 t/t4018/fortran-comment-legacy.ctx            |  1 +
 t/t4018/fortran-comment.ctx                   |  1 +
 t/t4018/fortran-external-function.ctx         |  1 +
 t/t4018/fortran-external-subroutine.ctx       |  1 +
 t/t4018/fortran-module-procedure.ctx          |  1 +
 t/t4018/fortran-module.ctx                    |  1 +
 t/t4018/fortran-program.ctx                   |  1 +
 t/t4018/fountain-scene.ctx                    |  1 +
 t/t4018/golang-complex-function.ctx           |  1 +
 t/t4018/golang-func.ctx                       |  1 +
 t/t4018/golang-interface.ctx                  |  1 +
 t/t4018/golang-long-func.ctx                  |  1 +
 t/t4018/golang-struct.ctx                     |  1 +
 t/t4018/java-class-member-function.ctx        |  1 +
 t/t4018/markdown-heading-indented.ctx         |  1 +
 t/t4018/markdown-heading-non-headings.ctx     |  1 +
 t/t4018/matlab-class-definition.ctx           |  1 +
 t/t4018/matlab-function.ctx                   |  1 +
 t/t4018/matlab-octave-section-1.ctx           |  1 +
 t/t4018/matlab-octave-section-2.ctx           |  1 +
 t/t4018/matlab-section.ctx                    |  1 +
 t/t4018/perl-skip-end-of-heredoc.ctx          |  1 +
 t/t4018/perl-skip-forward-decl.ctx            |  1 +
 t/t4018/perl-skip-sub-in-pod.ctx              |  1 +
 t/t4018/perl-sub-definition-kr-brace.ctx      |  1 +
 t/t4018/perl-sub-definition.ctx               |  1 +
 t/t4018/php-abstract-class.ctx                |  1 +
 t/t4018/php-abstract-method.ctx               |  1 +
 t/t4018/php-class.ctx                         |  1 +
 t/t4018/php-final-class.ctx                   |  1 +
 t/t4018/php-final-method.ctx                  |  1 +
 t/t4018/php-function.ctx                      |  1 +
 t/t4018/php-interface.ctx                     |  1 +
 t/t4018/php-method.ctx                        |  1 +
 t/t4018/php-trait.ctx                         |  1 +
 t/t4018/python-async-def.ctx                  |  1 +
 t/t4018/python-class.ctx                      |  1 +
 t/t4018/python-def.ctx                        |  1 +
 t/t4018/python-indented-async-def.ctx         |  1 +
 t/t4018/python-indented-class.ctx             |  1 +
 t/t4018/python-indented-def.ctx               |  1 +
 t/t4018/rust-fn.ctx                           |  1 +
 t/t4018/rust-impl.ctx                         |  1 +
 t/t4018/rust-macro-rules.ctx                  |  1 +
 t/t4018/rust-struct.ctx                       |  1 +
 t/t4018/rust-trait.ctx                        |  1 +
 124 files changed, 137 insertions(+), 14 deletions(-)
 create mode 100644 t/t4018/README.ctx
 create mode 100644 t/t4018/bash-arithmetic-function.ctx
 create mode 100644 t/t4018/bash-bashism-style-compact.ctx
 create mode 100644 t/t4018/bash-bashism-style-function.ctx
 create mode 100644 t/t4018/bash-bashism-style-whitespace.ctx
 create mode 100644 t/t4018/bash-conditional-function.ctx
 create mode 100644 t/t4018/bash-missing-parentheses.ctx
 create mode 100644 t/t4018/bash-mixed-style-compact.ctx
 create mode 100644 t/t4018/bash-mixed-style-function.ctx
 create mode 100644 t/t4018/bash-nested-functions.ctx
 create mode 100644 t/t4018/bash-other-characters.ctx
 create mode 100644 t/t4018/bash-posix-style-compact.ctx
 create mode 100644 t/t4018/bash-posix-style-function.ctx
 create mode 100644 t/t4018/bash-posix-style-whitespace.ctx
 create mode 100644 t/t4018/bash-subshell-function.ctx
 create mode 100644 t/t4018/bash-trailing-comment.ctx
 create mode 100644 t/t4018/cpp-c++-function.ctx
 create mode 100644 t/t4018/cpp-class-constructor-mem-init.ctx
 create mode 100644 t/t4018/cpp-class-constructor.ctx
 create mode 100644 t/t4018/cpp-class-definition-derived.ctx
 create mode 100644 t/t4018/cpp-class-definition.ctx
 create mode 100644 t/t4018/cpp-class-destructor.ctx
 create mode 100644 t/t4018/cpp-function-returning-global-type.ctx
 create mode 100644 t/t4018/cpp-function-returning-nested.ctx
 create mode 100644 t/t4018/cpp-function-returning-pointer.ctx
 create mode 100644 t/t4018/cpp-function-returning-reference.ctx
 create mode 100644 t/t4018/cpp-gnu-style-function.ctx
 create mode 100644 t/t4018/cpp-namespace-definition.ctx
 create mode 100644 t/t4018/cpp-operator-definition.ctx
 create mode 100644 t/t4018/cpp-skip-access-specifiers.ctx
 create mode 100644 t/t4018/cpp-skip-comment-block.ctx
 create mode 100644 t/t4018/cpp-skip-labels.ctx
 create mode 100644 t/t4018/cpp-struct-definition.ctx
 create mode 100644 t/t4018/cpp-struct-single-line.ctx
 create mode 100644 t/t4018/cpp-template-function-definition.ctx
 create mode 100644 t/t4018/cpp-union-definition.ctx
 create mode 100644 t/t4018/cpp-void-c-function.ctx
 create mode 100644 t/t4018/css-attribute-value-selector.ctx
 create mode 100644 t/t4018/css-block-level-@-statements.ctx
 create mode 100644 t/t4018/css-brace-in-col-1.ctx
 create mode 100644 t/t4018/css-class-selector.ctx
 create mode 100644 t/t4018/css-colon-eol.ctx
 create mode 100644 t/t4018/css-colon-selector.ctx
 create mode 100644 t/t4018/css-common.ctx
 create mode 100644 t/t4018/css-id-selector.ctx
 create mode 100644 t/t4018/css-long-selector-list.ctx
 create mode 100644 t/t4018/css-prop-sans-indent.ctx
 create mode 100644 t/t4018/css-root-selector.ctx
 create mode 100644 t/t4018/css-short-selector-list.ctx
 create mode 100644 t/t4018/css-trailing-space.ctx
 create mode 100644 t/t4018/custom1-pattern.ctx
 create mode 100644 t/t4018/custom2-match-to-end-of-line.ctx
 create mode 100644 t/t4018/custom3-alternation-in-pattern.ctx
 create mode 100644 t/t4018/dts-labels.ctx
 create mode 100644 t/t4018/dts-node-unitless.ctx
 create mode 100644 t/t4018/dts-nodes-boolean-prop.ctx
 create mode 100644 t/t4018/dts-nodes-comment1.ctx
 create mode 100644 t/t4018/dts-nodes-comment2.ctx
 create mode 100644 t/t4018/dts-nodes-multiline-prop.ctx
 create mode 100644 t/t4018/dts-nodes.ctx
 create mode 100644 t/t4018/dts-reference.ctx
 create mode 100644 t/t4018/dts-root-comment.ctx
 create mode 100644 t/t4018/dts-root.ctx
 create mode 100644 t/t4018/elixir-do-not-pick-end.ctx
 create mode 100644 t/t4018/elixir-ex-unit-test.ctx
 create mode 100644 t/t4018/elixir-function.ctx
 create mode 100644 t/t4018/elixir-macro.ctx
 create mode 100644 t/t4018/elixir-module-func.ctx
 create mode 100644 t/t4018/elixir-module.ctx
 create mode 100644 t/t4018/elixir-nested-module.ctx
 create mode 100644 t/t4018/elixir-private-function.ctx
 create mode 100644 t/t4018/elixir-protocol-implementation.ctx
 create mode 100644 t/t4018/elixir-protocol.ctx
 create mode 100644 t/t4018/fortran-block-data.ctx
 create mode 100644 t/t4018/fortran-comment-keyword.ctx
 create mode 100644 t/t4018/fortran-comment-legacy-star.ctx
 create mode 100644 t/t4018/fortran-comment-legacy.ctx
 create mode 100644 t/t4018/fortran-comment.ctx
 create mode 100644 t/t4018/fortran-external-function.ctx
 create mode 100644 t/t4018/fortran-external-subroutine.ctx
 create mode 100644 t/t4018/fortran-module-procedure.ctx
 create mode 100644 t/t4018/fortran-module.ctx
 create mode 100644 t/t4018/fortran-program.ctx
 create mode 100644 t/t4018/fountain-scene.ctx
 create mode 100644 t/t4018/golang-complex-function.ctx
 create mode 100644 t/t4018/golang-func.ctx
 create mode 100644 t/t4018/golang-interface.ctx
 create mode 100644 t/t4018/golang-long-func.ctx
 create mode 100644 t/t4018/golang-struct.ctx
 create mode 100644 t/t4018/java-class-member-function.ctx
 create mode 100644 t/t4018/markdown-heading-indented.ctx
 create mode 100644 t/t4018/markdown-heading-non-headings.ctx
 create mode 100644 t/t4018/matlab-class-definition.ctx
 create mode 100644 t/t4018/matlab-function.ctx
 create mode 100644 t/t4018/matlab-octave-section-1.ctx
 create mode 100644 t/t4018/matlab-octave-section-2.ctx
 create mode 100644 t/t4018/matlab-section.ctx
 create mode 100644 t/t4018/perl-skip-end-of-heredoc.ctx
 create mode 100644 t/t4018/perl-skip-forward-decl.ctx
 create mode 100644 t/t4018/perl-skip-sub-in-pod.ctx
 create mode 100644 t/t4018/perl-sub-definition-kr-brace.ctx
 create mode 100644 t/t4018/perl-sub-definition.ctx
 create mode 100644 t/t4018/php-abstract-class.ctx
 create mode 100644 t/t4018/php-abstract-method.ctx
 create mode 100644 t/t4018/php-class.ctx
 create mode 100644 t/t4018/php-final-class.ctx
 create mode 100644 t/t4018/php-final-method.ctx
 create mode 100644 t/t4018/php-function.ctx
 create mode 100644 t/t4018/php-interface.ctx
 create mode 100644 t/t4018/php-method.ctx
 create mode 100644 t/t4018/php-trait.ctx
 create mode 100644 t/t4018/python-async-def.ctx
 create mode 100644 t/t4018/python-class.ctx
 create mode 100644 t/t4018/python-def.ctx
 create mode 100644 t/t4018/python-indented-async-def.ctx
 create mode 100644 t/t4018/python-indented-class.ctx
 create mode 100644 t/t4018/python-indented-def.ctx
 create mode 100644 t/t4018/rust-fn.ctx
 create mode 100644 t/t4018/rust-impl.ctx
 create mode 100644 t/t4018/rust-macro-rules.ctx
 create mode 100644 t/t4018/rust-struct.ctx
 create mode 100644 t/t4018/rust-trait.ctx

diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index 5994c5b47a..3941316682 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -80,11 +80,12 @@ test_expect_success 'setup hunk header tests' '
 '
 
 # check each individual file
-for i in $(git ls-files)
+for i in $(git ls-files -- ':!*.ctx')
 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 $i.ctx ctx
 	"
 done
 
diff --git a/t/t4018/README b/t/t4018/README
index 2d25b2b4fc..d0619f76d4 100644
--- a/t/t4018/README
+++ b/t/t4018/README
@@ -1,15 +1,15 @@
-How to write RIGHT test 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.
 
 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.
-
-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.
-
-BTW, this tests that the head line goes to the hunk header, not the line
-of equal signs.
+The text that must appear in the hunk header must contains the word
+"RIGHT" by convention. The "LANG-whatever.ctx" file contains what we
+expect to appear in the hunk header. We munged away the starting "@@
+[...] @@" part of the line for ease of not having to hardcode the line
+numbers and offsets.
diff --git a/t/t4018/README.ctx b/t/t4018/README.ctx
new file mode 100644
index 0000000000..cd79384b04
--- /dev/null
+++ b/t/t4018/README.ctx
@@ -0,0 +1 @@
+description of the test.
diff --git a/t/t4018/bash-arithmetic-function.ctx b/t/t4018/bash-arithmetic-function.ctx
new file mode 100644
index 0000000000..811eac7d2f
--- /dev/null
+++ b/t/t4018/bash-arithmetic-function.ctx
@@ -0,0 +1 @@
+RIGHT()
diff --git a/t/t4018/bash-bashism-style-compact.ctx b/t/t4018/bash-bashism-style-compact.ctx
new file mode 100644
index 0000000000..4f8eac48c6
--- /dev/null
+++ b/t/t4018/bash-bashism-style-compact.ctx
@@ -0,0 +1 @@
+function RIGHT {
diff --git a/t/t4018/bash-bashism-style-function.ctx b/t/t4018/bash-bashism-style-function.ctx
new file mode 100644
index 0000000000..4f8eac48c6
--- /dev/null
+++ b/t/t4018/bash-bashism-style-function.ctx
@@ -0,0 +1 @@
+function RIGHT {
diff --git a/t/t4018/bash-bashism-style-whitespace.ctx b/t/t4018/bash-bashism-style-whitespace.ctx
new file mode 100644
index 0000000000..35dbd0220e
--- /dev/null
+++ b/t/t4018/bash-bashism-style-whitespace.ctx
@@ -0,0 +1 @@
+function 	RIGHT 	( 	) 	{
diff --git a/t/t4018/bash-conditional-function.ctx b/t/t4018/bash-conditional-function.ctx
new file mode 100644
index 0000000000..811eac7d2f
--- /dev/null
+++ b/t/t4018/bash-conditional-function.ctx
@@ -0,0 +1 @@
+RIGHT()
diff --git a/t/t4018/bash-missing-parentheses.ctx b/t/t4018/bash-missing-parentheses.ctx
new file mode 100644
index 0000000000..4f8eac48c6
--- /dev/null
+++ b/t/t4018/bash-missing-parentheses.ctx
@@ -0,0 +1 @@
+function RIGHT {
diff --git a/t/t4018/bash-mixed-style-compact.ctx b/t/t4018/bash-mixed-style-compact.ctx
new file mode 100644
index 0000000000..bba11074eb
--- /dev/null
+++ b/t/t4018/bash-mixed-style-compact.ctx
@@ -0,0 +1 @@
+function RIGHT(){
diff --git a/t/t4018/bash-mixed-style-function.ctx b/t/t4018/bash-mixed-style-function.ctx
new file mode 100644
index 0000000000..922b87a4aa
--- /dev/null
+++ b/t/t4018/bash-mixed-style-function.ctx
@@ -0,0 +1 @@
+function RIGHT() {
diff --git a/t/t4018/bash-nested-functions.ctx b/t/t4018/bash-nested-functions.ctx
new file mode 100644
index 0000000000..811eac7d2f
--- /dev/null
+++ b/t/t4018/bash-nested-functions.ctx
@@ -0,0 +1 @@
+RIGHT()
diff --git a/t/t4018/bash-other-characters.ctx b/t/t4018/bash-other-characters.ctx
new file mode 100644
index 0000000000..6a55317fdf
--- /dev/null
+++ b/t/t4018/bash-other-characters.ctx
@@ -0,0 +1 @@
+_RIGHT_0n()
diff --git a/t/t4018/bash-posix-style-compact.ctx b/t/t4018/bash-posix-style-compact.ctx
new file mode 100644
index 0000000000..811eac7d2f
--- /dev/null
+++ b/t/t4018/bash-posix-style-compact.ctx
@@ -0,0 +1 @@
+RIGHT()
diff --git a/t/t4018/bash-posix-style-function.ctx b/t/t4018/bash-posix-style-function.ctx
new file mode 100644
index 0000000000..811eac7d2f
--- /dev/null
+++ b/t/t4018/bash-posix-style-function.ctx
@@ -0,0 +1 @@
+RIGHT()
diff --git a/t/t4018/bash-posix-style-whitespace.ctx b/t/t4018/bash-posix-style-whitespace.ctx
new file mode 100644
index 0000000000..28f8698e14
--- /dev/null
+++ b/t/t4018/bash-posix-style-whitespace.ctx
@@ -0,0 +1 @@
+RIGHT 	( 	)
diff --git a/t/t4018/bash-subshell-function.ctx b/t/t4018/bash-subshell-function.ctx
new file mode 100644
index 0000000000..811eac7d2f
--- /dev/null
+++ b/t/t4018/bash-subshell-function.ctx
@@ -0,0 +1 @@
+RIGHT()
diff --git a/t/t4018/bash-trailing-comment.ctx b/t/t4018/bash-trailing-comment.ctx
new file mode 100644
index 0000000000..811eac7d2f
--- /dev/null
+++ b/t/t4018/bash-trailing-comment.ctx
@@ -0,0 +1 @@
+RIGHT()
diff --git a/t/t4018/cpp-c++-function.ctx b/t/t4018/cpp-c++-function.ctx
new file mode 100644
index 0000000000..337b49dbb3
--- /dev/null
+++ b/t/t4018/cpp-c++-function.ctx
@@ -0,0 +1 @@
+Item RIGHT::DoSomething( Args with_spaces )
diff --git a/t/t4018/cpp-class-constructor-mem-init.ctx b/t/t4018/cpp-class-constructor-mem-init.ctx
new file mode 100644
index 0000000000..6664b8b3d8
--- /dev/null
+++ b/t/t4018/cpp-class-constructor-mem-init.ctx
@@ -0,0 +1 @@
+Item::Item(int RIGHT) :
diff --git a/t/t4018/cpp-class-constructor.ctx b/t/t4018/cpp-class-constructor.ctx
new file mode 100644
index 0000000000..2dcadfc0ba
--- /dev/null
+++ b/t/t4018/cpp-class-constructor.ctx
@@ -0,0 +1 @@
+Item::Item(int RIGHT)
diff --git a/t/t4018/cpp-class-definition-derived.ctx b/t/t4018/cpp-class-definition-derived.ctx
new file mode 100644
index 0000000000..146f0a7b7c
--- /dev/null
+++ b/t/t4018/cpp-class-definition-derived.ctx
@@ -0,0 +1 @@
+class RIGHT :
diff --git a/t/t4018/cpp-class-definition.ctx b/t/t4018/cpp-class-definition.ctx
new file mode 100644
index 0000000000..54bff816d6
--- /dev/null
+++ b/t/t4018/cpp-class-definition.ctx
@@ -0,0 +1 @@
+class RIGHT
diff --git a/t/t4018/cpp-class-destructor.ctx b/t/t4018/cpp-class-destructor.ctx
new file mode 100644
index 0000000000..5390c17cf6
--- /dev/null
+++ b/t/t4018/cpp-class-destructor.ctx
@@ -0,0 +1 @@
+RIGHT::~RIGHT()
diff --git a/t/t4018/cpp-function-returning-global-type.ctx b/t/t4018/cpp-function-returning-global-type.ctx
new file mode 100644
index 0000000000..4dcdde25f4
--- /dev/null
+++ b/t/t4018/cpp-function-returning-global-type.ctx
@@ -0,0 +1 @@
+::Item get::it::RIGHT()
diff --git a/t/t4018/cpp-function-returning-nested.ctx b/t/t4018/cpp-function-returning-nested.ctx
new file mode 100644
index 0000000000..6ef73c8368
--- /dev/null
+++ b/t/t4018/cpp-function-returning-nested.ctx
@@ -0,0 +1 @@
+get::Item get::it::RIGHT()
diff --git a/t/t4018/cpp-function-returning-pointer.ctx b/t/t4018/cpp-function-returning-pointer.ctx
new file mode 100644
index 0000000000..bb0acce5c7
--- /dev/null
+++ b/t/t4018/cpp-function-returning-pointer.ctx
@@ -0,0 +1 @@
+const char *get_it_RIGHT(char *ptr)
diff --git a/t/t4018/cpp-function-returning-reference.ctx b/t/t4018/cpp-function-returning-reference.ctx
new file mode 100644
index 0000000000..76afe381fd
--- /dev/null
+++ b/t/t4018/cpp-function-returning-reference.ctx
@@ -0,0 +1 @@
+string& get::it::RIGHT(char *ptr)
diff --git a/t/t4018/cpp-gnu-style-function.ctx b/t/t4018/cpp-gnu-style-function.ctx
new file mode 100644
index 0000000000..1858287812
--- /dev/null
+++ b/t/t4018/cpp-gnu-style-function.ctx
@@ -0,0 +1 @@
+RIGHT(int arg)
diff --git a/t/t4018/cpp-namespace-definition.ctx b/t/t4018/cpp-namespace-definition.ctx
new file mode 100644
index 0000000000..14c29c4638
--- /dev/null
+++ b/t/t4018/cpp-namespace-definition.ctx
@@ -0,0 +1 @@
+namespace RIGHT
diff --git a/t/t4018/cpp-operator-definition.ctx b/t/t4018/cpp-operator-definition.ctx
new file mode 100644
index 0000000000..5b56778961
--- /dev/null
+++ b/t/t4018/cpp-operator-definition.ctx
@@ -0,0 +1 @@
+Value operator+(Value LEFT, Value RIGHT)
diff --git a/t/t4018/cpp-skip-access-specifiers.ctx b/t/t4018/cpp-skip-access-specifiers.ctx
new file mode 100644
index 0000000000..075bcd883b
--- /dev/null
+++ b/t/t4018/cpp-skip-access-specifiers.ctx
@@ -0,0 +1 @@
+class RIGHT : public Baseclass
diff --git a/t/t4018/cpp-skip-comment-block.ctx b/t/t4018/cpp-skip-comment-block.ctx
new file mode 100644
index 0000000000..656c59893d
--- /dev/null
+++ b/t/t4018/cpp-skip-comment-block.ctx
@@ -0,0 +1 @@
+struct item RIGHT(int i)
diff --git a/t/t4018/cpp-skip-labels.ctx b/t/t4018/cpp-skip-labels.ctx
new file mode 100644
index 0000000000..6b0635f7f7
--- /dev/null
+++ b/t/t4018/cpp-skip-labels.ctx
@@ -0,0 +1 @@
+void RIGHT (void)
diff --git a/t/t4018/cpp-struct-definition.ctx b/t/t4018/cpp-struct-definition.ctx
new file mode 100644
index 0000000000..48ed893279
--- /dev/null
+++ b/t/t4018/cpp-struct-definition.ctx
@@ -0,0 +1 @@
+struct RIGHT {
diff --git a/t/t4018/cpp-struct-single-line.ctx b/t/t4018/cpp-struct-single-line.ctx
new file mode 100644
index 0000000000..e3bc9d5017
--- /dev/null
+++ b/t/t4018/cpp-struct-single-line.ctx
@@ -0,0 +1 @@
+struct RIGHT_iterator_tag {};
diff --git a/t/t4018/cpp-template-function-definition.ctx b/t/t4018/cpp-template-function-definition.ctx
new file mode 100644
index 0000000000..c9da39cf65
--- /dev/null
+++ b/t/t4018/cpp-template-function-definition.ctx
@@ -0,0 +1 @@
+template<class T> int RIGHT(T arg)
diff --git a/t/t4018/cpp-union-definition.ctx b/t/t4018/cpp-union-definition.ctx
new file mode 100644
index 0000000000..2fc7b54fb8
--- /dev/null
+++ b/t/t4018/cpp-union-definition.ctx
@@ -0,0 +1 @@
+union RIGHT {
diff --git a/t/t4018/cpp-void-c-function.ctx b/t/t4018/cpp-void-c-function.ctx
new file mode 100644
index 0000000000..6b0635f7f7
--- /dev/null
+++ b/t/t4018/cpp-void-c-function.ctx
@@ -0,0 +1 @@
+void RIGHT (void)
diff --git a/t/t4018/css-attribute-value-selector.ctx b/t/t4018/css-attribute-value-selector.ctx
new file mode 100644
index 0000000000..7f8956251c
--- /dev/null
+++ b/t/t4018/css-attribute-value-selector.ctx
@@ -0,0 +1 @@
+[class*="RIGHT"] {
diff --git a/t/t4018/css-block-level-@-statements.ctx b/t/t4018/css-block-level-@-statements.ctx
new file mode 100644
index 0000000000..7f5e90468c
--- /dev/null
+++ b/t/t4018/css-block-level-@-statements.ctx
@@ -0,0 +1 @@
+@keyframes RIGHT {
diff --git a/t/t4018/css-brace-in-col-1.ctx b/t/t4018/css-brace-in-col-1.ctx
new file mode 100644
index 0000000000..91a9105c6a
--- /dev/null
+++ b/t/t4018/css-brace-in-col-1.ctx
@@ -0,0 +1 @@
+RIGHT label.control-label
diff --git a/t/t4018/css-class-selector.ctx b/t/t4018/css-class-selector.ctx
new file mode 100644
index 0000000000..ac7367d7f4
--- /dev/null
+++ b/t/t4018/css-class-selector.ctx
@@ -0,0 +1 @@
+.RIGHT {
diff --git a/t/t4018/css-colon-eol.ctx b/t/t4018/css-colon-eol.ctx
new file mode 100644
index 0000000000..b68493b9b0
--- /dev/null
+++ b/t/t4018/css-colon-eol.ctx
@@ -0,0 +1 @@
+RIGHT h1 {
diff --git a/t/t4018/css-colon-selector.ctx b/t/t4018/css-colon-selector.ctx
new file mode 100644
index 0000000000..00b1a5aefe
--- /dev/null
+++ b/t/t4018/css-colon-selector.ctx
@@ -0,0 +1 @@
+RIGHT a:hover {
diff --git a/t/t4018/css-common.ctx b/t/t4018/css-common.ctx
new file mode 100644
index 0000000000..43686b4081
--- /dev/null
+++ b/t/t4018/css-common.ctx
@@ -0,0 +1 @@
+RIGHT label.control-label {
diff --git a/t/t4018/css-id-selector.ctx b/t/t4018/css-id-selector.ctx
new file mode 100644
index 0000000000..ce19f6d8dc
--- /dev/null
+++ b/t/t4018/css-id-selector.ctx
@@ -0,0 +1 @@
+#RIGHT {
diff --git a/t/t4018/css-long-selector-list.ctx b/t/t4018/css-long-selector-list.ctx
new file mode 100644
index 0000000000..bc8d0fb62c
--- /dev/null
+++ b/t/t4018/css-long-selector-list.ctx
@@ -0,0 +1 @@
+div ul#RIGHT {
diff --git a/t/t4018/css-prop-sans-indent.ctx b/t/t4018/css-prop-sans-indent.ctx
new file mode 100644
index 0000000000..cc880b2f44
--- /dev/null
+++ b/t/t4018/css-prop-sans-indent.ctx
@@ -0,0 +1 @@
+RIGHT, label.control-label {
diff --git a/t/t4018/css-root-selector.ctx b/t/t4018/css-root-selector.ctx
new file mode 100644
index 0000000000..3010cded2a
--- /dev/null
+++ b/t/t4018/css-root-selector.ctx
@@ -0,0 +1 @@
+:RIGHT {
diff --git a/t/t4018/css-short-selector-list.ctx b/t/t4018/css-short-selector-list.ctx
new file mode 100644
index 0000000000..9e5d87d126
--- /dev/null
+++ b/t/t4018/css-short-selector-list.ctx
@@ -0,0 +1 @@
+label.control, div ul#RIGHT {
diff --git a/t/t4018/css-trailing-space.ctx b/t/t4018/css-trailing-space.ctx
new file mode 100644
index 0000000000..43686b4081
--- /dev/null
+++ b/t/t4018/css-trailing-space.ctx
@@ -0,0 +1 @@
+RIGHT label.control-label {
diff --git a/t/t4018/custom1-pattern.ctx b/t/t4018/custom1-pattern.ctx
new file mode 100644
index 0000000000..d1609cc9a6
--- /dev/null
+++ b/t/t4018/custom1-pattern.ctx
@@ -0,0 +1 @@
+int special, RIGHT;
diff --git a/t/t4018/custom2-match-to-end-of-line.ctx b/t/t4018/custom2-match-to-end-of-line.ctx
new file mode 100644
index 0000000000..8294c6e49b
--- /dev/null
+++ b/t/t4018/custom2-match-to-end-of-line.ctx
@@ -0,0 +1 @@
+RIGHT_Beer
diff --git a/t/t4018/custom3-alternation-in-pattern.ctx b/t/t4018/custom3-alternation-in-pattern.ctx
new file mode 100644
index 0000000000..2125474b68
--- /dev/null
+++ b/t/t4018/custom3-alternation-in-pattern.ctx
@@ -0,0 +1 @@
+public static void main(String RIGHT[])
diff --git a/t/t4018/dts-labels.ctx b/t/t4018/dts-labels.ctx
new file mode 100644
index 0000000000..48d9373cab
--- /dev/null
+++ b/t/t4018/dts-labels.ctx
@@ -0,0 +1 @@
+label2: RIGHT {
diff --git a/t/t4018/dts-node-unitless.ctx b/t/t4018/dts-node-unitless.ctx
new file mode 100644
index 0000000000..82c8683fa1
--- /dev/null
+++ b/t/t4018/dts-node-unitless.ctx
@@ -0,0 +1 @@
+RIGHT {
diff --git a/t/t4018/dts-nodes-boolean-prop.ctx b/t/t4018/dts-nodes-boolean-prop.ctx
new file mode 100644
index 0000000000..3a0232d55d
--- /dev/null
+++ b/t/t4018/dts-nodes-boolean-prop.ctx
@@ -0,0 +1 @@
+RIGHT@deadf00,4000 {
diff --git a/t/t4018/dts-nodes-comment1.ctx b/t/t4018/dts-nodes-comment1.ctx
new file mode 100644
index 0000000000..ec364600b1
--- /dev/null
+++ b/t/t4018/dts-nodes-comment1.ctx
@@ -0,0 +1 @@
+RIGHT@deadf00,4000 /* &a comment */ {
diff --git a/t/t4018/dts-nodes-comment2.ctx b/t/t4018/dts-nodes-comment2.ctx
new file mode 100644
index 0000000000..75f0d75258
--- /dev/null
+++ b/t/t4018/dts-nodes-comment2.ctx
@@ -0,0 +1 @@
+RIGHT@deadf00,4000 { /* a trailing comment */
diff --git a/t/t4018/dts-nodes-multiline-prop.ctx b/t/t4018/dts-nodes-multiline-prop.ctx
new file mode 100644
index 0000000000..3a0232d55d
--- /dev/null
+++ b/t/t4018/dts-nodes-multiline-prop.ctx
@@ -0,0 +1 @@
+RIGHT@deadf00,4000 {
diff --git a/t/t4018/dts-nodes.ctx b/t/t4018/dts-nodes.ctx
new file mode 100644
index 0000000000..3a0232d55d
--- /dev/null
+++ b/t/t4018/dts-nodes.ctx
@@ -0,0 +1 @@
+RIGHT@deadf00,4000 {
diff --git a/t/t4018/dts-reference.ctx b/t/t4018/dts-reference.ctx
new file mode 100644
index 0000000000..c1e13409ee
--- /dev/null
+++ b/t/t4018/dts-reference.ctx
@@ -0,0 +1 @@
+&RIGHT {
diff --git a/t/t4018/dts-root-comment.ctx b/t/t4018/dts-root-comment.ctx
new file mode 100644
index 0000000000..656053dd42
--- /dev/null
+++ b/t/t4018/dts-root-comment.ctx
@@ -0,0 +1 @@
+/ { RIGHT /* Technically just supposed to be a slash and brace */
diff --git a/t/t4018/dts-root.ctx b/t/t4018/dts-root.ctx
new file mode 100644
index 0000000000..656053dd42
--- /dev/null
+++ b/t/t4018/dts-root.ctx
@@ -0,0 +1 @@
+/ { RIGHT /* Technically just supposed to be a slash and brace */
diff --git a/t/t4018/elixir-do-not-pick-end.ctx b/t/t4018/elixir-do-not-pick-end.ctx
new file mode 100644
index 0000000000..8f28a7a689
--- /dev/null
+++ b/t/t4018/elixir-do-not-pick-end.ctx
@@ -0,0 +1 @@
+defmodule RIGHT do
diff --git a/t/t4018/elixir-ex-unit-test.ctx b/t/t4018/elixir-ex-unit-test.ctx
new file mode 100644
index 0000000000..a55e3de2cc
--- /dev/null
+++ b/t/t4018/elixir-ex-unit-test.ctx
@@ -0,0 +1 @@
+test "RIGHT" do
diff --git a/t/t4018/elixir-function.ctx b/t/t4018/elixir-function.ctx
new file mode 100644
index 0000000000..62aee9c8b1
--- /dev/null
+++ b/t/t4018/elixir-function.ctx
@@ -0,0 +1 @@
+def function(RIGHT, arg) do
diff --git a/t/t4018/elixir-macro.ctx b/t/t4018/elixir-macro.ctx
new file mode 100644
index 0000000000..fc1d3b85e8
--- /dev/null
+++ b/t/t4018/elixir-macro.ctx
@@ -0,0 +1 @@
+defmacro foo(RIGHT) do
diff --git a/t/t4018/elixir-module-func.ctx b/t/t4018/elixir-module-func.ctx
new file mode 100644
index 0000000000..8239214386
--- /dev/null
+++ b/t/t4018/elixir-module-func.ctx
@@ -0,0 +1 @@
+def fun(RIGHT) do
diff --git a/t/t4018/elixir-module.ctx b/t/t4018/elixir-module.ctx
new file mode 100644
index 0000000000..8f28a7a689
--- /dev/null
+++ b/t/t4018/elixir-module.ctx
@@ -0,0 +1 @@
+defmodule RIGHT do
diff --git a/t/t4018/elixir-nested-module.ctx b/t/t4018/elixir-nested-module.ctx
new file mode 100644
index 0000000000..3ffbdd18b1
--- /dev/null
+++ b/t/t4018/elixir-nested-module.ctx
@@ -0,0 +1 @@
+defmodule MyApp.RIGHT do
diff --git a/t/t4018/elixir-private-function.ctx b/t/t4018/elixir-private-function.ctx
new file mode 100644
index 0000000000..1c4eba44f7
--- /dev/null
+++ b/t/t4018/elixir-private-function.ctx
@@ -0,0 +1 @@
+defp function(RIGHT, arg) do
diff --git a/t/t4018/elixir-protocol-implementation.ctx b/t/t4018/elixir-protocol-implementation.ctx
new file mode 100644
index 0000000000..efb758aea6
--- /dev/null
+++ b/t/t4018/elixir-protocol-implementation.ctx
@@ -0,0 +1 @@
+defimpl RIGHT do
diff --git a/t/t4018/elixir-protocol.ctx b/t/t4018/elixir-protocol.ctx
new file mode 100644
index 0000000000..d0204e9f14
--- /dev/null
+++ b/t/t4018/elixir-protocol.ctx
@@ -0,0 +1 @@
+defprotocol RIGHT do
diff --git a/t/t4018/fortran-block-data.ctx b/t/t4018/fortran-block-data.ctx
new file mode 100644
index 0000000000..c3db084ccc
--- /dev/null
+++ b/t/t4018/fortran-block-data.ctx
@@ -0,0 +1 @@
+BLOCK DATA RIGHT
diff --git a/t/t4018/fortran-comment-keyword.ctx b/t/t4018/fortran-comment-keyword.ctx
new file mode 100644
index 0000000000..0b9220b355
--- /dev/null
+++ b/t/t4018/fortran-comment-keyword.ctx
@@ -0,0 +1 @@
+subroutine RIGHT (funcA, funcB)
diff --git a/t/t4018/fortran-comment-legacy-star.ctx b/t/t4018/fortran-comment-legacy-star.ctx
new file mode 100644
index 0000000000..6a34203f80
--- /dev/null
+++ b/t/t4018/fortran-comment-legacy-star.ctx
@@ -0,0 +1 @@
+subroutine RIGHT
diff --git a/t/t4018/fortran-comment-legacy.ctx b/t/t4018/fortran-comment-legacy.ctx
new file mode 100644
index 0000000000..6a34203f80
--- /dev/null
+++ b/t/t4018/fortran-comment-legacy.ctx
@@ -0,0 +1 @@
+subroutine RIGHT
diff --git a/t/t4018/fortran-comment.ctx b/t/t4018/fortran-comment.ctx
new file mode 100644
index 0000000000..6a34203f80
--- /dev/null
+++ b/t/t4018/fortran-comment.ctx
@@ -0,0 +1 @@
+subroutine RIGHT
diff --git a/t/t4018/fortran-external-function.ctx b/t/t4018/fortran-external-function.ctx
new file mode 100644
index 0000000000..56ec4d8eca
--- /dev/null
+++ b/t/t4018/fortran-external-function.ctx
@@ -0,0 +1 @@
+function RIGHT(a, b) result(c)
diff --git a/t/t4018/fortran-external-subroutine.ctx b/t/t4018/fortran-external-subroutine.ctx
new file mode 100644
index 0000000000..6a34203f80
--- /dev/null
+++ b/t/t4018/fortran-external-subroutine.ctx
@@ -0,0 +1 @@
+subroutine RIGHT
diff --git a/t/t4018/fortran-module-procedure.ctx b/t/t4018/fortran-module-procedure.ctx
new file mode 100644
index 0000000000..4f5ff2e4b8
--- /dev/null
+++ b/t/t4018/fortran-module-procedure.ctx
@@ -0,0 +1 @@
+module RIGHT
diff --git a/t/t4018/fortran-module.ctx b/t/t4018/fortran-module.ctx
new file mode 100644
index 0000000000..4f5ff2e4b8
--- /dev/null
+++ b/t/t4018/fortran-module.ctx
@@ -0,0 +1 @@
+module RIGHT
diff --git a/t/t4018/fortran-program.ctx b/t/t4018/fortran-program.ctx
new file mode 100644
index 0000000000..c4e844df30
--- /dev/null
+++ b/t/t4018/fortran-program.ctx
@@ -0,0 +1 @@
+program RIGHT
diff --git a/t/t4018/fountain-scene.ctx b/t/t4018/fountain-scene.ctx
new file mode 100644
index 0000000000..bf10171418
--- /dev/null
+++ b/t/t4018/fountain-scene.ctx
@@ -0,0 +1 @@
+EXT. STREET RIGHT OUTSIDE - DAY
diff --git a/t/t4018/golang-complex-function.ctx b/t/t4018/golang-complex-function.ctx
new file mode 100644
index 0000000000..8e8d5582ff
--- /dev/null
+++ b/t/t4018/golang-complex-function.ctx
@@ -0,0 +1 @@
+func (t *Test) RIGHT(a Type) (Type, error) {
diff --git a/t/t4018/golang-func.ctx b/t/t4018/golang-func.ctx
new file mode 100644
index 0000000000..88bc823813
--- /dev/null
+++ b/t/t4018/golang-func.ctx
@@ -0,0 +1 @@
+func RIGHT() {
diff --git a/t/t4018/golang-interface.ctx b/t/t4018/golang-interface.ctx
new file mode 100644
index 0000000000..2d07f5a383
--- /dev/null
+++ b/t/t4018/golang-interface.ctx
@@ -0,0 +1 @@
+type RIGHT interface {
diff --git a/t/t4018/golang-long-func.ctx b/t/t4018/golang-long-func.ctx
new file mode 100644
index 0000000000..25635e712e
--- /dev/null
+++ b/t/t4018/golang-long-func.ctx
@@ -0,0 +1 @@
+func RIGHT(aVeryVeryVeryLongVariableName AVeryVeryVeryLongType,
diff --git a/t/t4018/golang-struct.ctx b/t/t4018/golang-struct.ctx
new file mode 100644
index 0000000000..8a1240699d
--- /dev/null
+++ b/t/t4018/golang-struct.ctx
@@ -0,0 +1 @@
+type RIGHT struct {
diff --git a/t/t4018/java-class-member-function.ctx b/t/t4018/java-class-member-function.ctx
new file mode 100644
index 0000000000..2125474b68
--- /dev/null
+++ b/t/t4018/java-class-member-function.ctx
@@ -0,0 +1 @@
+public static void main(String RIGHT[])
diff --git a/t/t4018/markdown-heading-indented.ctx b/t/t4018/markdown-heading-indented.ctx
new file mode 100644
index 0000000000..5938336743
--- /dev/null
+++ b/t/t4018/markdown-heading-indented.ctx
@@ -0,0 +1 @@
+   ### RIGHT
diff --git a/t/t4018/markdown-heading-non-headings.ctx b/t/t4018/markdown-heading-non-headings.ctx
new file mode 100644
index 0000000000..7e2165be6e
--- /dev/null
+++ b/t/t4018/markdown-heading-non-headings.ctx
@@ -0,0 +1 @@
+# RIGHT
diff --git a/t/t4018/matlab-class-definition.ctx b/t/t4018/matlab-class-definition.ctx
new file mode 100644
index 0000000000..5dd5b45628
--- /dev/null
+++ b/t/t4018/matlab-class-definition.ctx
@@ -0,0 +1 @@
+classdef RIGHT
diff --git a/t/t4018/matlab-function.ctx b/t/t4018/matlab-function.ctx
new file mode 100644
index 0000000000..72d2350b13
--- /dev/null
+++ b/t/t4018/matlab-function.ctx
@@ -0,0 +1 @@
+function y = RIGHT()
diff --git a/t/t4018/matlab-octave-section-1.ctx b/t/t4018/matlab-octave-section-1.ctx
new file mode 100644
index 0000000000..ca9b349f94
--- /dev/null
+++ b/t/t4018/matlab-octave-section-1.ctx
@@ -0,0 +1 @@
+%%% RIGHT section
diff --git a/t/t4018/matlab-octave-section-2.ctx b/t/t4018/matlab-octave-section-2.ctx
new file mode 100644
index 0000000000..5cbb77faf5
--- /dev/null
+++ b/t/t4018/matlab-octave-section-2.ctx
@@ -0,0 +1 @@
+## RIGHT section
diff --git a/t/t4018/matlab-section.ctx b/t/t4018/matlab-section.ctx
new file mode 100644
index 0000000000..e83fee6f4d
--- /dev/null
+++ b/t/t4018/matlab-section.ctx
@@ -0,0 +1 @@
+%% RIGHT section
diff --git a/t/t4018/perl-skip-end-of-heredoc.ctx b/t/t4018/perl-skip-end-of-heredoc.ctx
new file mode 100644
index 0000000000..c15f4b78bd
--- /dev/null
+++ b/t/t4018/perl-skip-end-of-heredoc.ctx
@@ -0,0 +1 @@
+sub RIGHTwithheredocument {
diff --git a/t/t4018/perl-skip-forward-decl.ctx b/t/t4018/perl-skip-forward-decl.ctx
new file mode 100644
index 0000000000..e0c51599ad
--- /dev/null
+++ b/t/t4018/perl-skip-forward-decl.ctx
@@ -0,0 +1 @@
+package RIGHT;
diff --git a/t/t4018/perl-skip-sub-in-pod.ctx b/t/t4018/perl-skip-sub-in-pod.ctx
new file mode 100644
index 0000000000..abddd76655
--- /dev/null
+++ b/t/t4018/perl-skip-sub-in-pod.ctx
@@ -0,0 +1 @@
+=head1 SYNOPSIS_RIGHT
diff --git a/t/t4018/perl-sub-definition-kr-brace.ctx b/t/t4018/perl-sub-definition-kr-brace.ctx
new file mode 100644
index 0000000000..7e5aee5cde
--- /dev/null
+++ b/t/t4018/perl-sub-definition-kr-brace.ctx
@@ -0,0 +1 @@
+sub RIGHT
diff --git a/t/t4018/perl-sub-definition.ctx b/t/t4018/perl-sub-definition.ctx
new file mode 100644
index 0000000000..d49a63598e
--- /dev/null
+++ b/t/t4018/perl-sub-definition.ctx
@@ -0,0 +1 @@
+sub RIGHT {
diff --git a/t/t4018/php-abstract-class.ctx b/t/t4018/php-abstract-class.ctx
new file mode 100644
index 0000000000..f572d2129b
--- /dev/null
+++ b/t/t4018/php-abstract-class.ctx
@@ -0,0 +1 @@
+abstract class RIGHT
diff --git a/t/t4018/php-abstract-method.ctx b/t/t4018/php-abstract-method.ctx
new file mode 100644
index 0000000000..14cb6df42e
--- /dev/null
+++ b/t/t4018/php-abstract-method.ctx
@@ -0,0 +1 @@
+abstract public function RIGHT(): ?string
diff --git a/t/t4018/php-class.ctx b/t/t4018/php-class.ctx
new file mode 100644
index 0000000000..54bff816d6
--- /dev/null
+++ b/t/t4018/php-class.ctx
@@ -0,0 +1 @@
+class RIGHT
diff --git a/t/t4018/php-final-class.ctx b/t/t4018/php-final-class.ctx
new file mode 100644
index 0000000000..4d59fb749b
--- /dev/null
+++ b/t/t4018/php-final-class.ctx
@@ -0,0 +1 @@
+final class RIGHT
diff --git a/t/t4018/php-final-method.ctx b/t/t4018/php-final-method.ctx
new file mode 100644
index 0000000000..b7da8f8082
--- /dev/null
+++ b/t/t4018/php-final-method.ctx
@@ -0,0 +1 @@
+final public function RIGHT(): string
diff --git a/t/t4018/php-function.ctx b/t/t4018/php-function.ctx
new file mode 100644
index 0000000000..c5f3e55302
--- /dev/null
+++ b/t/t4018/php-function.ctx
@@ -0,0 +1 @@
+function RIGHT()
diff --git a/t/t4018/php-interface.ctx b/t/t4018/php-interface.ctx
new file mode 100644
index 0000000000..a45fa0532a
--- /dev/null
+++ b/t/t4018/php-interface.ctx
@@ -0,0 +1 @@
+interface RIGHT
diff --git a/t/t4018/php-method.ctx b/t/t4018/php-method.ctx
new file mode 100644
index 0000000000..eb1659ff9f
--- /dev/null
+++ b/t/t4018/php-method.ctx
@@ -0,0 +1 @@
+public static function RIGHT()
diff --git a/t/t4018/php-trait.ctx b/t/t4018/php-trait.ctx
new file mode 100644
index 0000000000..57aa4c6267
--- /dev/null
+++ b/t/t4018/php-trait.ctx
@@ -0,0 +1 @@
+trait RIGHT
diff --git a/t/t4018/python-async-def.ctx b/t/t4018/python-async-def.ctx
new file mode 100644
index 0000000000..468c548bbe
--- /dev/null
+++ b/t/t4018/python-async-def.ctx
@@ -0,0 +1 @@
+async def RIGHT(pi: int = 3.14):
diff --git a/t/t4018/python-class.ctx b/t/t4018/python-class.ctx
new file mode 100644
index 0000000000..a40b755e29
--- /dev/null
+++ b/t/t4018/python-class.ctx
@@ -0,0 +1 @@
+class RIGHT(int, str):
diff --git a/t/t4018/python-def.ctx b/t/t4018/python-def.ctx
new file mode 100644
index 0000000000..a1a9cbad63
--- /dev/null
+++ b/t/t4018/python-def.ctx
@@ -0,0 +1 @@
+def RIGHT(pi: int = 3.14):
diff --git a/t/t4018/python-indented-async-def.ctx b/t/t4018/python-indented-async-def.ctx
new file mode 100644
index 0000000000..d393620a1e
--- /dev/null
+++ b/t/t4018/python-indented-async-def.ctx
@@ -0,0 +1 @@
+async def RIGHT(self, x: int):
diff --git a/t/t4018/python-indented-class.ctx b/t/t4018/python-indented-class.ctx
new file mode 100644
index 0000000000..0881c84dba
--- /dev/null
+++ b/t/t4018/python-indented-class.ctx
@@ -0,0 +1 @@
+class RIGHT:
diff --git a/t/t4018/python-indented-def.ctx b/t/t4018/python-indented-def.ctx
new file mode 100644
index 0000000000..6e5a44b391
--- /dev/null
+++ b/t/t4018/python-indented-def.ctx
@@ -0,0 +1 @@
+def RIGHT(self, x: int):
diff --git a/t/t4018/rust-fn.ctx b/t/t4018/rust-fn.ctx
new file mode 100644
index 0000000000..baa37cf253
--- /dev/null
+++ b/t/t4018/rust-fn.ctx
@@ -0,0 +1 @@
+pub(self) fn RIGHT<T>(x: &[T]) where T: Debug {
diff --git a/t/t4018/rust-impl.ctx b/t/t4018/rust-impl.ctx
new file mode 100644
index 0000000000..5344c35f3f
--- /dev/null
+++ b/t/t4018/rust-impl.ctx
@@ -0,0 +1 @@
+impl<'a, T: AsRef<[u8]>>  std::RIGHT for Git<'a> {
diff --git a/t/t4018/rust-macro-rules.ctx b/t/t4018/rust-macro-rules.ctx
new file mode 100644
index 0000000000..7520463aa0
--- /dev/null
+++ b/t/t4018/rust-macro-rules.ctx
@@ -0,0 +1 @@
+macro_rules! RIGHT {
diff --git a/t/t4018/rust-struct.ctx b/t/t4018/rust-struct.ctx
new file mode 100644
index 0000000000..c1e09dc808
--- /dev/null
+++ b/t/t4018/rust-struct.ctx
@@ -0,0 +1 @@
+pub(super) struct RIGHT<'a> {
diff --git a/t/t4018/rust-trait.ctx b/t/t4018/rust-trait.ctx
new file mode 100644
index 0000000000..6af803db29
--- /dev/null
+++ b/t/t4018/rust-trait.ctx
@@ -0,0 +1 @@
+unsafe trait RIGHT<T> {
-- 
2.30.0.284.gd98b1dd5eaa7


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

* [PATCH v2 10/27] blame tests: don't rely on t/t4018/ directory
  2021-02-15  0:52       ` [PATCH 00/20] userdiff: refactor + test + doc + misc improvements Ævar Arnfjörð Bjarmason
                           ` (9 preceding siblings ...)
  2021-02-15 15:44         ` [PATCH v2 09/27] userdiff tests: match full hunk headers Ævar Arnfjörð Bjarmason
@ 2021-02-15 15:44         ` Ævar Arnfjörð Bjarmason
  2021-02-16 23:49           ` Junio C Hamano
  2021-02-15 15:44         ` [PATCH v2 11/27] blame tests: simplify userdiff driver test Ævar Arnfjörð Bjarmason
                           ` (16 subsequent siblings)
  27 siblings, 1 reply; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15 15:44 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers, Eric Sunshine, Chris Torek,
	Ævar Arnfjörð Bjarmason

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 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.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 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 29ce89090d..04a2c58594 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


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

* [PATCH v2 11/27] blame tests: simplify userdiff driver test
  2021-02-15  0:52       ` [PATCH 00/20] userdiff: refactor + test + doc + misc improvements Ævar Arnfjörð Bjarmason
                           ` (10 preceding siblings ...)
  2021-02-15 15:44         ` [PATCH v2 10/27] blame tests: don't rely on t/t4018/ directory Ævar Arnfjörð Bjarmason
@ 2021-02-15 15:44         ` Ævar Arnfjörð Bjarmason
  2021-02-15 17:23           ` Johannes Sixt
  2021-02-15 15:44         ` [PATCH v2 12/27] userdiff tests: rewrite hunk header test infrastructure Ævar Arnfjörð Bjarmason
                           ` (15 subsequent siblings)
  27 siblings, 1 reply; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15 15:44 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers, Eric Sunshine, Chris Torek,
	Ævar Arnfjörð Bjarmason

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 <avarab@gmail.com>
---
 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' '
-- 
2.30.0.284.gd98b1dd5eaa7


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

* [PATCH v2 12/27] userdiff tests: rewrite hunk header test infrastructure
  2021-02-15  0:52       ` [PATCH 00/20] userdiff: refactor + test + doc + misc improvements Ævar Arnfjörð Bjarmason
                           ` (11 preceding siblings ...)
  2021-02-15 15:44         ` [PATCH v2 11/27] blame tests: simplify userdiff driver test Ævar Arnfjörð Bjarmason
@ 2021-02-15 15:44         ` Ævar Arnfjörð Bjarmason
  2021-02-15 17:53           ` Johannes Sixt
  2021-02-16 18:35           ` Junio C Hamano
  2021-02-15 15:44         ` [PATCH v2 13/27] userdiff tests: do config teardown in test_diff_funcname() Ævar Arnfjörð Bjarmason
                           ` (14 subsequent siblings)
  27 siblings, 2 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15 15:44 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers, Eric Sunshine, Chris Torek,
	Ævar Arnfjörð Bjarmason

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 <avarab@gmail.com>
---
 t/t4018-diff-funcname.sh                      |  59 +++--
 t/t4018/README                                |  15 --
 t/t4018/README.ctx                            |   1 -
 t/t4018/bash-arithmetic-function              |   4 -
 t/t4018/bash-arithmetic-function.ctx          |   1 -
 t/t4018/bash-bashism-style-compact            |   6 -
 t/t4018/bash-bashism-style-compact.ctx        |   1 -
 t/t4018/bash-bashism-style-function           |   4 -
 t/t4018/bash-bashism-style-function.ctx       |   1 -
 t/t4018/bash-bashism-style-whitespace         |   4 -
 t/t4018/bash-bashism-style-whitespace.ctx     |   1 -
 t/t4018/bash-conditional-function             |   4 -
 t/t4018/bash-conditional-function.ctx         |   1 -
 t/t4018/bash-missing-parentheses              |   6 -
 t/t4018/bash-missing-parentheses.ctx          |   1 -
 t/t4018/bash-mixed-style-compact              |   4 -
 t/t4018/bash-mixed-style-compact.ctx          |   1 -
 t/t4018/bash-mixed-style-function             |   4 -
 t/t4018/bash-mixed-style-function.ctx         |   1 -
 t/t4018/bash-nested-functions                 |   6 -
 t/t4018/bash-nested-functions.ctx             |   1 -
 t/t4018/bash-other-characters                 |   4 -
 t/t4018/bash-other-characters.ctx             |   1 -
 t/t4018/bash-posix-style-compact              |   4 -
 t/t4018/bash-posix-style-compact.ctx          |   1 -
 t/t4018/bash-posix-style-function             |   4 -
 t/t4018/bash-posix-style-function.ctx         |   1 -
 t/t4018/bash-posix-style-whitespace           |   4 -
 t/t4018/bash-posix-style-whitespace.ctx       |   1 -
 t/t4018/bash-subshell-function                |   4 -
 t/t4018/bash-subshell-function.ctx            |   1 -
 t/t4018/bash-trailing-comment                 |   4 -
 t/t4018/bash-trailing-comment.ctx             |   1 -
 t/t4018/bash.sh                               | 160 ++++++++++++
 t/t4018/cpp-c++-function                      |   4 -
 t/t4018/cpp-c++-function.ctx                  |   1 -
 t/t4018/cpp-class-constructor                 |   4 -
 t/t4018/cpp-class-constructor-mem-init        |   5 -
 t/t4018/cpp-class-constructor-mem-init.ctx    |   1 -
 t/t4018/cpp-class-constructor.ctx             |   1 -
 t/t4018/cpp-class-definition                  |   4 -
 t/t4018/cpp-class-definition-derived          |   5 -
 t/t4018/cpp-class-definition-derived.ctx      |   1 -
 t/t4018/cpp-class-definition.ctx              |   1 -
 t/t4018/cpp-class-destructor                  |   4 -
 t/t4018/cpp-class-destructor.ctx              |   1 -
 t/t4018/cpp-function-returning-global-type    |   4 -
 .../cpp-function-returning-global-type.ctx    |   1 -
 t/t4018/cpp-function-returning-nested         |   5 -
 t/t4018/cpp-function-returning-nested.ctx     |   1 -
 t/t4018/cpp-function-returning-pointer        |   4 -
 t/t4018/cpp-function-returning-pointer.ctx    |   1 -
 t/t4018/cpp-function-returning-reference      |   4 -
 t/t4018/cpp-function-returning-reference.ctx  |   1 -
 t/t4018/cpp-gnu-style-function                |   5 -
 t/t4018/cpp-gnu-style-function.ctx            |   1 -
 t/t4018/cpp-namespace-definition              |   4 -
 t/t4018/cpp-namespace-definition.ctx          |   1 -
 t/t4018/cpp-operator-definition               |   4 -
 t/t4018/cpp-operator-definition.ctx           |   1 -
 t/t4018/cpp-skip-access-specifiers            |   8 -
 t/t4018/cpp-skip-access-specifiers.ctx        |   1 -
 t/t4018/cpp-skip-comment-block                |   9 -
 t/t4018/cpp-skip-comment-block.ctx            |   1 -
 t/t4018/cpp-skip-labels                       |   8 -
 t/t4018/cpp-skip-labels.ctx                   |   1 -
 t/t4018/cpp-struct-definition                 |   9 -
 t/t4018/cpp-struct-definition.ctx             |   1 -
 t/t4018/cpp-struct-single-line                |   7 -
 t/t4018/cpp-struct-single-line.ctx            |   1 -
 t/t4018/cpp-template-function-definition      |   4 -
 t/t4018/cpp-template-function-definition.ctx  |   1 -
 t/t4018/cpp-union-definition                  |   4 -
 t/t4018/cpp-union-definition.ctx              |   1 -
 t/t4018/cpp-void-c-function                   |   4 -
 t/t4018/cpp-void-c-function.ctx               |   1 -
 t/t4018/cpp.sh                                | 239 ++++++++++++++++++
 t/t4018/css-attribute-value-selector          |   4 -
 t/t4018/css-attribute-value-selector.ctx      |   1 -
 t/t4018/css-block-level-@-statements          |  10 -
 t/t4018/css-block-level-@-statements.ctx      |   1 -
 t/t4018/css-brace-in-col-1                    |   5 -
 t/t4018/css-brace-in-col-1.ctx                |   1 -
 t/t4018/css-class-selector                    |   4 -
 t/t4018/css-class-selector.ctx                |   1 -
 t/t4018/css-colon-eol                         |   4 -
 t/t4018/css-colon-eol.ctx                     |   1 -
 t/t4018/css-colon-selector                    |   5 -
 t/t4018/css-colon-selector.ctx                |   1 -
 t/t4018/css-common                            |   4 -
 t/t4018/css-common.ctx                        |   1 -
 t/t4018/css-id-selector                       |   4 -
 t/t4018/css-id-selector.ctx                   |   1 -
 t/t4018/css-long-selector-list                |   6 -
 t/t4018/css-long-selector-list.ctx            |   1 -
 t/t4018/css-prop-sans-indent                  |   5 -
 t/t4018/css-prop-sans-indent.ctx              |   1 -
 t/t4018/css-root-selector                     |   4 -
 t/t4018/css-root-selector.ctx                 |   1 -
 t/t4018/css-short-selector-list               |   4 -
 t/t4018/css-short-selector-list.ctx           |   1 -
 t/t4018/css-trailing-space                    |   5 -
 t/t4018/css-trailing-space.ctx                |   1 -
 t/t4018/css.sh                                | 146 +++++++++++
 t/t4018/custom1-pattern.ctx                   |   1 -
 t/t4018/{custom1-pattern => custom1.sh}       |  10 +
 t/t4018/custom2-match-to-end-of-line          |   8 -
 t/t4018/custom2-match-to-end-of-line.ctx      |   1 -
 t/t4018/custom2.sh                            |  18 ++
 t/t4018/custom3-alternation-in-pattern.ctx    |   1 -
 ...tom3-alternation-in-pattern => custom3.sh} |  10 +
 t/t4018/dts-labels                            |   9 -
 t/t4018/dts-labels.ctx                        |   1 -
 t/t4018/dts-node-unitless                     |   8 -
 t/t4018/dts-node-unitless.ctx                 |   1 -
 t/t4018/dts-nodes                             |   8 -
 t/t4018/dts-nodes-boolean-prop                |   9 -
 t/t4018/dts-nodes-boolean-prop.ctx            |   1 -
 t/t4018/dts-nodes-comment1                    |   8 -
 t/t4018/dts-nodes-comment1.ctx                |   1 -
 t/t4018/dts-nodes-comment2                    |   8 -
 t/t4018/dts-nodes-comment2.ctx                |   1 -
 t/t4018/dts-nodes-multiline-prop              |  13 -
 t/t4018/dts-nodes-multiline-prop.ctx          |   1 -
 t/t4018/dts-nodes.ctx                         |   1 -
 t/t4018/dts-reference                         |   9 -
 t/t4018/dts-reference.ctx                     |   1 -
 t/t4018/dts-root                              |   5 -
 t/t4018/dts-root-comment                      |   8 -
 t/t4018/dts-root-comment.ctx                  |   1 -
 t/t4018/dts-root.ctx                          |   1 -
 t/t4018/dts.sh                                | 149 +++++++++++
 t/t4018/elixir-do-not-pick-end                |   5 -
 t/t4018/elixir-do-not-pick-end.ctx            |   1 -
 t/t4018/elixir-ex-unit-test                   |   6 -
 t/t4018/elixir-ex-unit-test.ctx               |   1 -
 t/t4018/elixir-function                       |   5 -
 t/t4018/elixir-function.ctx                   |   1 -
 t/t4018/elixir-macro                          |   5 -
 t/t4018/elixir-macro.ctx                      |   1 -
 t/t4018/elixir-module                         |   9 -
 t/t4018/elixir-module-func                    |   8 -
 t/t4018/elixir-module-func.ctx                |   1 -
 t/t4018/elixir-module.ctx                     |   1 -
 t/t4018/elixir-nested-module                  |   9 -
 t/t4018/elixir-nested-module.ctx              |   1 -
 t/t4018/elixir-private-function               |   5 -
 t/t4018/elixir-private-function.ctx           |   1 -
 t/t4018/elixir-protocol                       |   6 -
 t/t4018/elixir-protocol-implementation        |   5 -
 t/t4018/elixir-protocol-implementation.ctx    |   1 -
 t/t4018/elixir-protocol.ctx                   |   1 -
 t/t4018/elixir.sh                             | 127 ++++++++++
 t/t4018/fortran-block-data                    |   5 -
 t/t4018/fortran-block-data.ctx                |   1 -
 t/t4018/fortran-comment                       |  13 -
 t/t4018/fortran-comment-keyword               |  14 -
 t/t4018/fortran-comment-keyword.ctx           |   1 -
 t/t4018/fortran-comment-legacy                |  13 -
 t/t4018/fortran-comment-legacy-star           |  13 -
 t/t4018/fortran-comment-legacy-star.ctx       |   1 -
 t/t4018/fortran-comment-legacy.ctx            |   1 -
 t/t4018/fortran-comment.ctx                   |   1 -
 t/t4018/fortran-external-function             |   9 -
 t/t4018/fortran-external-function.ctx         |   1 -
 t/t4018/fortran-external-subroutine           |   5 -
 t/t4018/fortran-external-subroutine.ctx       |   1 -
 t/t4018/fortran-module                        |   5 -
 t/t4018/fortran-module-procedure              |  13 -
 t/t4018/fortran-module-procedure.ctx          |   1 -
 t/t4018/fortran-module.ctx                    |   1 -
 t/t4018/fortran-program                       |   5 -
 t/t4018/fortran-program.ctx                   |   1 -
 t/t4018/fortran.sh                            | 159 ++++++++++++
 t/t4018/fountain-scene                        |   4 -
 t/t4018/fountain-scene.ctx                    |   1 -
 t/t4018/fountain.sh                           |  14 +
 t/t4018/golang-complex-function               |   8 -
 t/t4018/golang-complex-function.ctx           |   1 -
 t/t4018/golang-func                           |   4 -
 t/t4018/golang-func.ctx                       |   1 -
 t/t4018/golang-interface                      |   4 -
 t/t4018/golang-interface.ctx                  |   1 -
 t/t4018/golang-long-func                      |   5 -
 t/t4018/golang-long-func.ctx                  |   1 -
 t/t4018/golang-struct                         |   4 -
 t/t4018/golang-struct.ctx                     |   1 -
 t/t4018/golang.sh                             |  59 +++++
 t/t4018/java-class-member-function            |   8 -
 t/t4018/java-class-member-function.ctx        |   1 -
 t/t4018/java.sh                               |  18 ++
 t/t4018/markdown-heading-indented             |   6 -
 t/t4018/markdown-heading-indented.ctx         |   1 -
 t/t4018/markdown-heading-non-headings         |  17 --
 t/t4018/markdown-heading-non-headings.ctx     |   1 -
 t/t4018/markdown.sh                           |  39 +++
 t/t4018/matlab-class-definition               |   5 -
 t/t4018/matlab-class-definition.ctx           |   1 -
 t/t4018/matlab-function                       |   4 -
 t/t4018/matlab-function.ctx                   |   1 -
 t/t4018/matlab-octave-section-1               |   3 -
 t/t4018/matlab-octave-section-1.ctx           |   1 -
 t/t4018/matlab-octave-section-2               |   3 -
 t/t4018/matlab-octave-section-2.ctx           |   1 -
 t/t4018/matlab-section                        |   3 -
 t/t4018/matlab-section.ctx                    |   1 -
 t/t4018/matlab.sh                             |  52 ++++
 t/t4018/perl-skip-end-of-heredoc              |   8 -
 t/t4018/perl-skip-end-of-heredoc.ctx          |   1 -
 t/t4018/perl-skip-forward-decl                |  10 -
 t/t4018/perl-skip-forward-decl.ctx            |   1 -
 t/t4018/perl-skip-sub-in-pod                  |  18 --
 t/t4018/perl-skip-sub-in-pod.ctx              |   1 -
 t/t4018/perl-sub-definition                   |   4 -
 t/t4018/perl-sub-definition-kr-brace          |   4 -
 t/t4018/perl-sub-definition-kr-brace.ctx      |   1 -
 t/t4018/perl-sub-definition.ctx               |   1 -
 t/t4018/perl.sh                               |  78 ++++++
 t/t4018/php-abstract-class                    |   4 -
 t/t4018/php-abstract-class.ctx                |   1 -
 t/t4018/php-abstract-method                   |   7 -
 t/t4018/php-abstract-method.ctx               |   1 -
 t/t4018/php-class                             |   4 -
 t/t4018/php-class.ctx                         |   1 -
 t/t4018/php-final-class                       |   4 -
 t/t4018/php-final-class.ctx                   |   1 -
 t/t4018/php-final-method                      |   7 -
 t/t4018/php-final-method.ctx                  |   1 -
 t/t4018/php-function                          |   4 -
 t/t4018/php-function.ctx                      |   1 -
 t/t4018/php-interface                         |   4 -
 t/t4018/php-interface.ctx                     |   1 -
 t/t4018/php-method                            |   7 -
 t/t4018/php-method.ctx                        |   1 -
 t/t4018/php-trait                             |   7 -
 t/t4018/php-trait.ctx                         |   1 -
 t/t4018/php.sh                                | 106 ++++++++
 t/t4018/python-async-def                      |   4 -
 t/t4018/python-async-def.ctx                  |   1 -
 t/t4018/python-class                          |   4 -
 t/t4018/python-class.ctx                      |   1 -
 t/t4018/python-def                            |   4 -
 t/t4018/python-def.ctx                        |   1 -
 t/t4018/python-indented-async-def             |   7 -
 t/t4018/python-indented-async-def.ctx         |   1 -
 t/t4018/python-indented-class                 |   5 -
 t/t4018/python-indented-class.ctx             |   1 -
 t/t4018/python-indented-def                   |   7 -
 t/t4018/python-indented-def.ctx               |   1 -
 t/t4018/python.sh                             |  71 ++++++
 t/t4018/rust-fn                               |   5 -
 t/t4018/rust-fn.ctx                           |   1 -
 t/t4018/rust-impl                             |   5 -
 t/t4018/rust-impl.ctx                         |   1 -
 t/t4018/rust-macro-rules                      |   6 -
 t/t4018/rust-macro-rules.ctx                  |   1 -
 t/t4018/rust-struct                           |   5 -
 t/t4018/rust-struct.ctx                       |   1 -
 t/t4018/rust-trait                            |   5 -
 t/t4018/rust-trait.ctx                        |   1 -
 t/t4018/rust.sh                               |  60 +++++
 261 files changed, 1549 insertions(+), 879 deletions(-)
 delete mode 100644 t/t4018/README
 delete mode 100644 t/t4018/README.ctx
 delete mode 100644 t/t4018/bash-arithmetic-function
 delete mode 100644 t/t4018/bash-arithmetic-function.ctx
 delete mode 100644 t/t4018/bash-bashism-style-compact
 delete mode 100644 t/t4018/bash-bashism-style-compact.ctx
 delete mode 100644 t/t4018/bash-bashism-style-function
 delete mode 100644 t/t4018/bash-bashism-style-function.ctx
 delete mode 100644 t/t4018/bash-bashism-style-whitespace
 delete mode 100644 t/t4018/bash-bashism-style-whitespace.ctx
 delete mode 100644 t/t4018/bash-conditional-function
 delete mode 100644 t/t4018/bash-conditional-function.ctx
 delete mode 100644 t/t4018/bash-missing-parentheses
 delete mode 100644 t/t4018/bash-missing-parentheses.ctx
 delete mode 100644 t/t4018/bash-mixed-style-compact
 delete mode 100644 t/t4018/bash-mixed-style-compact.ctx
 delete mode 100644 t/t4018/bash-mixed-style-function
 delete mode 100644 t/t4018/bash-mixed-style-function.ctx
 delete mode 100644 t/t4018/bash-nested-functions
 delete mode 100644 t/t4018/bash-nested-functions.ctx
 delete mode 100644 t/t4018/bash-other-characters
 delete mode 100644 t/t4018/bash-other-characters.ctx
 delete mode 100644 t/t4018/bash-posix-style-compact
 delete mode 100644 t/t4018/bash-posix-style-compact.ctx
 delete mode 100644 t/t4018/bash-posix-style-function
 delete mode 100644 t/t4018/bash-posix-style-function.ctx
 delete mode 100644 t/t4018/bash-posix-style-whitespace
 delete mode 100644 t/t4018/bash-posix-style-whitespace.ctx
 delete mode 100644 t/t4018/bash-subshell-function
 delete mode 100644 t/t4018/bash-subshell-function.ctx
 delete mode 100644 t/t4018/bash-trailing-comment
 delete mode 100644 t/t4018/bash-trailing-comment.ctx
 create mode 100755 t/t4018/bash.sh
 delete mode 100644 t/t4018/cpp-c++-function
 delete mode 100644 t/t4018/cpp-c++-function.ctx
 delete mode 100644 t/t4018/cpp-class-constructor
 delete mode 100644 t/t4018/cpp-class-constructor-mem-init
 delete mode 100644 t/t4018/cpp-class-constructor-mem-init.ctx
 delete mode 100644 t/t4018/cpp-class-constructor.ctx
 delete mode 100644 t/t4018/cpp-class-definition
 delete mode 100644 t/t4018/cpp-class-definition-derived
 delete mode 100644 t/t4018/cpp-class-definition-derived.ctx
 delete mode 100644 t/t4018/cpp-class-definition.ctx
 delete mode 100644 t/t4018/cpp-class-destructor
 delete mode 100644 t/t4018/cpp-class-destructor.ctx
 delete mode 100644 t/t4018/cpp-function-returning-global-type
 delete mode 100644 t/t4018/cpp-function-returning-global-type.ctx
 delete mode 100644 t/t4018/cpp-function-returning-nested
 delete mode 100644 t/t4018/cpp-function-returning-nested.ctx
 delete mode 100644 t/t4018/cpp-function-returning-pointer
 delete mode 100644 t/t4018/cpp-function-returning-pointer.ctx
 delete mode 100644 t/t4018/cpp-function-returning-reference
 delete mode 100644 t/t4018/cpp-function-returning-reference.ctx
 delete mode 100644 t/t4018/cpp-gnu-style-function
 delete mode 100644 t/t4018/cpp-gnu-style-function.ctx
 delete mode 100644 t/t4018/cpp-namespace-definition
 delete mode 100644 t/t4018/cpp-namespace-definition.ctx
 delete mode 100644 t/t4018/cpp-operator-definition
 delete mode 100644 t/t4018/cpp-operator-definition.ctx
 delete mode 100644 t/t4018/cpp-skip-access-specifiers
 delete mode 100644 t/t4018/cpp-skip-access-specifiers.ctx
 delete mode 100644 t/t4018/cpp-skip-comment-block
 delete mode 100644 t/t4018/cpp-skip-comment-block.ctx
 delete mode 100644 t/t4018/cpp-skip-labels
 delete mode 100644 t/t4018/cpp-skip-labels.ctx
 delete mode 100644 t/t4018/cpp-struct-definition
 delete mode 100644 t/t4018/cpp-struct-definition.ctx
 delete mode 100644 t/t4018/cpp-struct-single-line
 delete mode 100644 t/t4018/cpp-struct-single-line.ctx
 delete mode 100644 t/t4018/cpp-template-function-definition
 delete mode 100644 t/t4018/cpp-template-function-definition.ctx
 delete mode 100644 t/t4018/cpp-union-definition
 delete mode 100644 t/t4018/cpp-union-definition.ctx
 delete mode 100644 t/t4018/cpp-void-c-function
 delete mode 100644 t/t4018/cpp-void-c-function.ctx
 create mode 100755 t/t4018/cpp.sh
 delete mode 100644 t/t4018/css-attribute-value-selector
 delete mode 100644 t/t4018/css-attribute-value-selector.ctx
 delete mode 100644 t/t4018/css-block-level-@-statements
 delete mode 100644 t/t4018/css-block-level-@-statements.ctx
 delete mode 100644 t/t4018/css-brace-in-col-1
 delete mode 100644 t/t4018/css-brace-in-col-1.ctx
 delete mode 100644 t/t4018/css-class-selector
 delete mode 100644 t/t4018/css-class-selector.ctx
 delete mode 100644 t/t4018/css-colon-eol
 delete mode 100644 t/t4018/css-colon-eol.ctx
 delete mode 100644 t/t4018/css-colon-selector
 delete mode 100644 t/t4018/css-colon-selector.ctx
 delete mode 100644 t/t4018/css-common
 delete mode 100644 t/t4018/css-common.ctx
 delete mode 100644 t/t4018/css-id-selector
 delete mode 100644 t/t4018/css-id-selector.ctx
 delete mode 100644 t/t4018/css-long-selector-list
 delete mode 100644 t/t4018/css-long-selector-list.ctx
 delete mode 100644 t/t4018/css-prop-sans-indent
 delete mode 100644 t/t4018/css-prop-sans-indent.ctx
 delete mode 100644 t/t4018/css-root-selector
 delete mode 100644 t/t4018/css-root-selector.ctx
 delete mode 100644 t/t4018/css-short-selector-list
 delete mode 100644 t/t4018/css-short-selector-list.ctx
 delete mode 100644 t/t4018/css-trailing-space
 delete mode 100644 t/t4018/css-trailing-space.ctx
 create mode 100755 t/t4018/css.sh
 delete mode 100644 t/t4018/custom1-pattern.ctx
 rename t/t4018/{custom1-pattern => custom1.sh} (71%)
 mode change 100644 => 100755
 delete mode 100644 t/t4018/custom2-match-to-end-of-line
 delete mode 100644 t/t4018/custom2-match-to-end-of-line.ctx
 create mode 100755 t/t4018/custom2.sh
 delete mode 100644 t/t4018/custom3-alternation-in-pattern.ctx
 rename t/t4018/{custom3-alternation-in-pattern => custom3.sh} (66%)
 mode change 100644 => 100755
 delete mode 100644 t/t4018/dts-labels
 delete mode 100644 t/t4018/dts-labels.ctx
 delete mode 100644 t/t4018/dts-node-unitless
 delete mode 100644 t/t4018/dts-node-unitless.ctx
 delete mode 100644 t/t4018/dts-nodes
 delete mode 100644 t/t4018/dts-nodes-boolean-prop
 delete mode 100644 t/t4018/dts-nodes-boolean-prop.ctx
 delete mode 100644 t/t4018/dts-nodes-comment1
 delete mode 100644 t/t4018/dts-nodes-comment1.ctx
 delete mode 100644 t/t4018/dts-nodes-comment2
 delete mode 100644 t/t4018/dts-nodes-comment2.ctx
 delete mode 100644 t/t4018/dts-nodes-multiline-prop
 delete mode 100644 t/t4018/dts-nodes-multiline-prop.ctx
 delete mode 100644 t/t4018/dts-nodes.ctx
 delete mode 100644 t/t4018/dts-reference
 delete mode 100644 t/t4018/dts-reference.ctx
 delete mode 100644 t/t4018/dts-root
 delete mode 100644 t/t4018/dts-root-comment
 delete mode 100644 t/t4018/dts-root-comment.ctx
 delete mode 100644 t/t4018/dts-root.ctx
 create mode 100755 t/t4018/dts.sh
 delete mode 100644 t/t4018/elixir-do-not-pick-end
 delete mode 100644 t/t4018/elixir-do-not-pick-end.ctx
 delete mode 100644 t/t4018/elixir-ex-unit-test
 delete mode 100644 t/t4018/elixir-ex-unit-test.ctx
 delete mode 100644 t/t4018/elixir-function
 delete mode 100644 t/t4018/elixir-function.ctx
 delete mode 100644 t/t4018/elixir-macro
 delete mode 100644 t/t4018/elixir-macro.ctx
 delete mode 100644 t/t4018/elixir-module
 delete mode 100644 t/t4018/elixir-module-func
 delete mode 100644 t/t4018/elixir-module-func.ctx
 delete mode 100644 t/t4018/elixir-module.ctx
 delete mode 100644 t/t4018/elixir-nested-module
 delete mode 100644 t/t4018/elixir-nested-module.ctx
 delete mode 100644 t/t4018/elixir-private-function
 delete mode 100644 t/t4018/elixir-private-function.ctx
 delete mode 100644 t/t4018/elixir-protocol
 delete mode 100644 t/t4018/elixir-protocol-implementation
 delete mode 100644 t/t4018/elixir-protocol-implementation.ctx
 delete mode 100644 t/t4018/elixir-protocol.ctx
 create mode 100755 t/t4018/elixir.sh
 delete mode 100644 t/t4018/fortran-block-data
 delete mode 100644 t/t4018/fortran-block-data.ctx
 delete mode 100644 t/t4018/fortran-comment
 delete mode 100644 t/t4018/fortran-comment-keyword
 delete mode 100644 t/t4018/fortran-comment-keyword.ctx
 delete mode 100644 t/t4018/fortran-comment-legacy
 delete mode 100644 t/t4018/fortran-comment-legacy-star
 delete mode 100644 t/t4018/fortran-comment-legacy-star.ctx
 delete mode 100644 t/t4018/fortran-comment-legacy.ctx
 delete mode 100644 t/t4018/fortran-comment.ctx
 delete mode 100644 t/t4018/fortran-external-function
 delete mode 100644 t/t4018/fortran-external-function.ctx
 delete mode 100644 t/t4018/fortran-external-subroutine
 delete mode 100644 t/t4018/fortran-external-subroutine.ctx
 delete mode 100644 t/t4018/fortran-module
 delete mode 100644 t/t4018/fortran-module-procedure
 delete mode 100644 t/t4018/fortran-module-procedure.ctx
 delete mode 100644 t/t4018/fortran-module.ctx
 delete mode 100644 t/t4018/fortran-program
 delete mode 100644 t/t4018/fortran-program.ctx
 create mode 100755 t/t4018/fortran.sh
 delete mode 100644 t/t4018/fountain-scene
 delete mode 100644 t/t4018/fountain-scene.ctx
 create mode 100755 t/t4018/fountain.sh
 delete mode 100644 t/t4018/golang-complex-function
 delete mode 100644 t/t4018/golang-complex-function.ctx
 delete mode 100644 t/t4018/golang-func
 delete mode 100644 t/t4018/golang-func.ctx
 delete mode 100644 t/t4018/golang-interface
 delete mode 100644 t/t4018/golang-interface.ctx
 delete mode 100644 t/t4018/golang-long-func
 delete mode 100644 t/t4018/golang-long-func.ctx
 delete mode 100644 t/t4018/golang-struct
 delete mode 100644 t/t4018/golang-struct.ctx
 create mode 100755 t/t4018/golang.sh
 delete mode 100644 t/t4018/java-class-member-function
 delete mode 100644 t/t4018/java-class-member-function.ctx
 create mode 100755 t/t4018/java.sh
 delete mode 100644 t/t4018/markdown-heading-indented
 delete mode 100644 t/t4018/markdown-heading-indented.ctx
 delete mode 100644 t/t4018/markdown-heading-non-headings
 delete mode 100644 t/t4018/markdown-heading-non-headings.ctx
 create mode 100755 t/t4018/markdown.sh
 delete mode 100644 t/t4018/matlab-class-definition
 delete mode 100644 t/t4018/matlab-class-definition.ctx
 delete mode 100644 t/t4018/matlab-function
 delete mode 100644 t/t4018/matlab-function.ctx
 delete mode 100644 t/t4018/matlab-octave-section-1
 delete mode 100644 t/t4018/matlab-octave-section-1.ctx
 delete mode 100644 t/t4018/matlab-octave-section-2
 delete mode 100644 t/t4018/matlab-octave-section-2.ctx
 delete mode 100644 t/t4018/matlab-section
 delete mode 100644 t/t4018/matlab-section.ctx
 create mode 100755 t/t4018/matlab.sh
 delete mode 100644 t/t4018/perl-skip-end-of-heredoc
 delete mode 100644 t/t4018/perl-skip-end-of-heredoc.ctx
 delete mode 100644 t/t4018/perl-skip-forward-decl
 delete mode 100644 t/t4018/perl-skip-forward-decl.ctx
 delete mode 100644 t/t4018/perl-skip-sub-in-pod
 delete mode 100644 t/t4018/perl-skip-sub-in-pod.ctx
 delete mode 100644 t/t4018/perl-sub-definition
 delete mode 100644 t/t4018/perl-sub-definition-kr-brace
 delete mode 100644 t/t4018/perl-sub-definition-kr-brace.ctx
 delete mode 100644 t/t4018/perl-sub-definition.ctx
 create mode 100755 t/t4018/perl.sh
 delete mode 100644 t/t4018/php-abstract-class
 delete mode 100644 t/t4018/php-abstract-class.ctx
 delete mode 100644 t/t4018/php-abstract-method
 delete mode 100644 t/t4018/php-abstract-method.ctx
 delete mode 100644 t/t4018/php-class
 delete mode 100644 t/t4018/php-class.ctx
 delete mode 100644 t/t4018/php-final-class
 delete mode 100644 t/t4018/php-final-class.ctx
 delete mode 100644 t/t4018/php-final-method
 delete mode 100644 t/t4018/php-final-method.ctx
 delete mode 100644 t/t4018/php-function
 delete mode 100644 t/t4018/php-function.ctx
 delete mode 100644 t/t4018/php-interface
 delete mode 100644 t/t4018/php-interface.ctx
 delete mode 100644 t/t4018/php-method
 delete mode 100644 t/t4018/php-method.ctx
 delete mode 100644 t/t4018/php-trait
 delete mode 100644 t/t4018/php-trait.ctx
 create mode 100755 t/t4018/php.sh
 delete mode 100644 t/t4018/python-async-def
 delete mode 100644 t/t4018/python-async-def.ctx
 delete mode 100644 t/t4018/python-class
 delete mode 100644 t/t4018/python-class.ctx
 delete mode 100644 t/t4018/python-def
 delete mode 100644 t/t4018/python-def.ctx
 delete mode 100644 t/t4018/python-indented-async-def
 delete mode 100644 t/t4018/python-indented-async-def.ctx
 delete mode 100644 t/t4018/python-indented-class
 delete mode 100644 t/t4018/python-indented-class.ctx
 delete mode 100644 t/t4018/python-indented-def
 delete mode 100644 t/t4018/python-indented-def.ctx
 create mode 100755 t/t4018/python.sh
 delete mode 100644 t/t4018/rust-fn
 delete mode 100644 t/t4018/rust-fn.ctx
 delete mode 100644 t/t4018/rust-impl
 delete mode 100644 t/t4018/rust-impl.ctx
 delete mode 100644 t/t4018/rust-macro-rules
 delete mode 100644 t/t4018/rust-macro-rules.ctx
 delete mode 100644 t/t4018/rust-struct
 delete mode 100644 t/t4018/rust-struct.ctx
 delete mode 100644 t/t4018/rust-trait
 delete mode 100644 t/t4018/rust-trait.ctx
 create mode 100755 t/t4018/rust.sh

diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index 3941316682..decf7961f9 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -60,33 +60,42 @@ test_expect_success 'last regexp must not be negated' '
 	test_i18ngrep ": Last expression must not be negated:" msg
 '
 
-test_expect_success 'setup hunk header tests' '
-	for i in $diffpatterns
-	do
-		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)
-	do
-		sed -e "s/ChangeMe/IWasChanged/" <"$TEST_DIRECTORY/t4018/$i" >"$i" || return 1
-	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" &&
+		sed -e "s/ChangeMe/IWasChanged/" <"$what" >tmp &&
+		mv tmp "$what"
+	' &&
+
+	test_expect_success "$desc" '
+		git diff -U1 "$what" >diff &&
+		sed -n -e "s/^.*@@$//p" -e "s/^.*@@ //p" <diff >actual &&
+		test_cmp expected actual
+	'
+}
 
-# check each individual file
-for i in $(git ls-files -- ':!*.ctx')
+for what in $diffpatterns
 do
-	test_expect_success "hunk header: $i" "
-		git diff -U1 $i >diff &&
-		sed -n -e 's/^.*@@$//p' -e 's/^.*@@ //p' <diff >ctx &&
-		test_cmp $i.ctx ctx
-	"
+	test="$TEST_DIRECTORY/t4018/$what.sh"
+	if ! test -e "$test"
+	then
+		test_expect_failure "$what: no tests" 'false'
+		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
deleted file mode 100644
index d0619f76d4..0000000000
--- a/t/t4018/README
+++ /dev/null
@@ -1,15 +0,0 @@
-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 contains the word
-"RIGHT" by convention. The "LANG-whatever.ctx" file contains what we
-expect to appear in the hunk header. We munged away the starting "@@
-[...] @@" part of the line for ease of not having to hardcode the line
-numbers and offsets.
diff --git a/t/t4018/README.ctx b/t/t4018/README.ctx
deleted file mode 100644
index cd79384b04..0000000000
--- a/t/t4018/README.ctx
+++ /dev/null
@@ -1 +0,0 @@
-description of the test.
diff --git a/t/t4018/bash-arithmetic-function b/t/t4018/bash-arithmetic-function
deleted file mode 100644
index c0b276cb50..0000000000
--- a/t/t4018/bash-arithmetic-function
+++ /dev/null
@@ -1,4 +0,0 @@
-RIGHT() ((
-
-    ChangeMe = "$x" + "$y"
-))
diff --git a/t/t4018/bash-arithmetic-function.ctx b/t/t4018/bash-arithmetic-function.ctx
deleted file mode 100644
index 811eac7d2f..0000000000
--- a/t/t4018/bash-arithmetic-function.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT()
diff --git a/t/t4018/bash-bashism-style-compact b/t/t4018/bash-bashism-style-compact
deleted file mode 100644
index 1ca3126f61..0000000000
--- a/t/t4018/bash-bashism-style-compact
+++ /dev/null
@@ -1,6 +0,0 @@
-function RIGHT {
-    function InvalidSyntax{
-        :
-        echo 'ChangeMe'
-    }
-}
diff --git a/t/t4018/bash-bashism-style-compact.ctx b/t/t4018/bash-bashism-style-compact.ctx
deleted file mode 100644
index 4f8eac48c6..0000000000
--- a/t/t4018/bash-bashism-style-compact.ctx
+++ /dev/null
@@ -1 +0,0 @@
-function RIGHT {
diff --git a/t/t4018/bash-bashism-style-function b/t/t4018/bash-bashism-style-function
deleted file mode 100644
index f1de4fa831..0000000000
--- a/t/t4018/bash-bashism-style-function
+++ /dev/null
@@ -1,4 +0,0 @@
-function RIGHT {
-    :
-    echo 'ChangeMe'
-}
diff --git a/t/t4018/bash-bashism-style-function.ctx b/t/t4018/bash-bashism-style-function.ctx
deleted file mode 100644
index 4f8eac48c6..0000000000
--- a/t/t4018/bash-bashism-style-function.ctx
+++ /dev/null
@@ -1 +0,0 @@
-function RIGHT {
diff --git a/t/t4018/bash-bashism-style-whitespace b/t/t4018/bash-bashism-style-whitespace
deleted file mode 100644
index ade85dd3a5..0000000000
--- a/t/t4018/bash-bashism-style-whitespace
+++ /dev/null
@@ -1,4 +0,0 @@
-	 function 	RIGHT 	( 	) 	{
-
-	    ChangeMe
-	 }
diff --git a/t/t4018/bash-bashism-style-whitespace.ctx b/t/t4018/bash-bashism-style-whitespace.ctx
deleted file mode 100644
index 35dbd0220e..0000000000
--- a/t/t4018/bash-bashism-style-whitespace.ctx
+++ /dev/null
@@ -1 +0,0 @@
-function 	RIGHT 	( 	) 	{
diff --git a/t/t4018/bash-conditional-function b/t/t4018/bash-conditional-function
deleted file mode 100644
index c5949e829b..0000000000
--- a/t/t4018/bash-conditional-function
+++ /dev/null
@@ -1,4 +0,0 @@
-RIGHT() [[ \
-
-    "$a" > "$ChangeMe"
-]]
diff --git a/t/t4018/bash-conditional-function.ctx b/t/t4018/bash-conditional-function.ctx
deleted file mode 100644
index 811eac7d2f..0000000000
--- a/t/t4018/bash-conditional-function.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT()
diff --git a/t/t4018/bash-missing-parentheses b/t/t4018/bash-missing-parentheses
deleted file mode 100644
index 8c8a05dd7a..0000000000
--- a/t/t4018/bash-missing-parentheses
+++ /dev/null
@@ -1,6 +0,0 @@
-function RIGHT {
-    functionInvalidSyntax {
-        :
-        echo 'ChangeMe'
-    }
-}
diff --git a/t/t4018/bash-missing-parentheses.ctx b/t/t4018/bash-missing-parentheses.ctx
deleted file mode 100644
index 4f8eac48c6..0000000000
--- a/t/t4018/bash-missing-parentheses.ctx
+++ /dev/null
@@ -1 +0,0 @@
-function RIGHT {
diff --git a/t/t4018/bash-mixed-style-compact b/t/t4018/bash-mixed-style-compact
deleted file mode 100644
index d9364cba67..0000000000
--- a/t/t4018/bash-mixed-style-compact
+++ /dev/null
@@ -1,4 +0,0 @@
-function RIGHT(){
-    :
-    echo 'ChangeMe'
-}
diff --git a/t/t4018/bash-mixed-style-compact.ctx b/t/t4018/bash-mixed-style-compact.ctx
deleted file mode 100644
index bba11074eb..0000000000
--- a/t/t4018/bash-mixed-style-compact.ctx
+++ /dev/null
@@ -1 +0,0 @@
-function RIGHT(){
diff --git a/t/t4018/bash-mixed-style-function b/t/t4018/bash-mixed-style-function
deleted file mode 100644
index 555f9b2466..0000000000
--- a/t/t4018/bash-mixed-style-function
+++ /dev/null
@@ -1,4 +0,0 @@
-function RIGHT() {
-
-    ChangeMe
-}
diff --git a/t/t4018/bash-mixed-style-function.ctx b/t/t4018/bash-mixed-style-function.ctx
deleted file mode 100644
index 922b87a4aa..0000000000
--- a/t/t4018/bash-mixed-style-function.ctx
+++ /dev/null
@@ -1 +0,0 @@
-function RIGHT() {
diff --git a/t/t4018/bash-nested-functions b/t/t4018/bash-nested-functions
deleted file mode 100644
index 2c9237ead4..0000000000
--- a/t/t4018/bash-nested-functions
+++ /dev/null
@@ -1,6 +0,0 @@
-outer() {
-    RIGHT() {
-        :
-        echo 'ChangeMe'
-    }
-}
diff --git a/t/t4018/bash-nested-functions.ctx b/t/t4018/bash-nested-functions.ctx
deleted file mode 100644
index 811eac7d2f..0000000000
--- a/t/t4018/bash-nested-functions.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT()
diff --git a/t/t4018/bash-other-characters b/t/t4018/bash-other-characters
deleted file mode 100644
index a3f390d525..0000000000
--- a/t/t4018/bash-other-characters
+++ /dev/null
@@ -1,4 +0,0 @@
-_RIGHT_0n() {
-
-    ChangeMe
-}
diff --git a/t/t4018/bash-other-characters.ctx b/t/t4018/bash-other-characters.ctx
deleted file mode 100644
index 6a55317fdf..0000000000
--- a/t/t4018/bash-other-characters.ctx
+++ /dev/null
@@ -1 +0,0 @@
-_RIGHT_0n()
diff --git a/t/t4018/bash-posix-style-compact b/t/t4018/bash-posix-style-compact
deleted file mode 100644
index 045bd2029b..0000000000
--- a/t/t4018/bash-posix-style-compact
+++ /dev/null
@@ -1,4 +0,0 @@
-RIGHT(){
-
-    ChangeMe
-}
diff --git a/t/t4018/bash-posix-style-compact.ctx b/t/t4018/bash-posix-style-compact.ctx
deleted file mode 100644
index 811eac7d2f..0000000000
--- a/t/t4018/bash-posix-style-compact.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT()
diff --git a/t/t4018/bash-posix-style-function b/t/t4018/bash-posix-style-function
deleted file mode 100644
index a4d144856e..0000000000
--- a/t/t4018/bash-posix-style-function
+++ /dev/null
@@ -1,4 +0,0 @@
-RIGHT() {
-
-    ChangeMe
-}
diff --git a/t/t4018/bash-posix-style-function.ctx b/t/t4018/bash-posix-style-function.ctx
deleted file mode 100644
index 811eac7d2f..0000000000
--- a/t/t4018/bash-posix-style-function.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT()
diff --git a/t/t4018/bash-posix-style-whitespace b/t/t4018/bash-posix-style-whitespace
deleted file mode 100644
index 4d984f0aa4..0000000000
--- a/t/t4018/bash-posix-style-whitespace
+++ /dev/null
@@ -1,4 +0,0 @@
-	 RIGHT 	( 	) 	{
-
-	    ChangeMe
-	 }
diff --git a/t/t4018/bash-posix-style-whitespace.ctx b/t/t4018/bash-posix-style-whitespace.ctx
deleted file mode 100644
index 28f8698e14..0000000000
--- a/t/t4018/bash-posix-style-whitespace.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT 	( 	)
diff --git a/t/t4018/bash-subshell-function b/t/t4018/bash-subshell-function
deleted file mode 100644
index 80baa09484..0000000000
--- a/t/t4018/bash-subshell-function
+++ /dev/null
@@ -1,4 +0,0 @@
-RIGHT() (
-
-    ChangeMe=2
-)
diff --git a/t/t4018/bash-subshell-function.ctx b/t/t4018/bash-subshell-function.ctx
deleted file mode 100644
index 811eac7d2f..0000000000
--- a/t/t4018/bash-subshell-function.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT()
diff --git a/t/t4018/bash-trailing-comment b/t/t4018/bash-trailing-comment
deleted file mode 100644
index f1edbeda31..0000000000
--- a/t/t4018/bash-trailing-comment
+++ /dev/null
@@ -1,4 +0,0 @@
-RIGHT() { # Comment
-
-    ChangeMe
-}
diff --git a/t/t4018/bash-trailing-comment.ctx b/t/t4018/bash-trailing-comment.ctx
deleted file mode 100644
index 811eac7d2f..0000000000
--- a/t/t4018/bash-trailing-comment.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT()
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
+
+test_diff_funcname 'bash: bashism style whitespace' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+function 	RIGHT 	( 	) 	{
+EOF_HUNK
+	 function 	RIGHT 	( 	) 	{
+
+	    ChangeMe
+	 }
+EOF_TEST
+
+test_diff_funcname 'bash: conditional function' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT()
+EOF_HUNK
+RIGHT() [[ \
+
+    "$a" > "$ChangeMe"
+]]
+EOF_TEST
+
+test_diff_funcname 'bash: missing parentheses' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+function RIGHT {
+EOF_HUNK
+function RIGHT {
+    functionInvalidSyntax {
+        :
+        echo 'ChangeMe'
+    }
+}
+EOF_TEST
+
+test_diff_funcname 'bash: mixed style compact' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+function RIGHT(){
+EOF_HUNK
+function RIGHT(){
+    :
+    echo 'ChangeMe'
+}
+EOF_TEST
+
+test_diff_funcname 'bash: mixed style function' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+function RIGHT() {
+EOF_HUNK
+function RIGHT() {
+
+    ChangeMe
+}
+EOF_TEST
+
+test_diff_funcname 'bash: nested functions' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT()
+EOF_HUNK
+outer() {
+    RIGHT() {
+        :
+        echo 'ChangeMe'
+    }
+}
+EOF_TEST
+
+test_diff_funcname 'bash: other characters' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+_RIGHT_0n()
+EOF_HUNK
+_RIGHT_0n() {
+
+    ChangeMe
+}
+EOF_TEST
+
+test_diff_funcname 'bash: posix style compact' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT()
+EOF_HUNK
+RIGHT(){
+
+    ChangeMe
+}
+EOF_TEST
+
+test_diff_funcname 'bash: posix style function' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT()
+EOF_HUNK
+RIGHT() {
+
+    ChangeMe
+}
+EOF_TEST
+
+test_diff_funcname 'bash: posix style whitespace' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT 	( 	)
+EOF_HUNK
+	 RIGHT 	( 	) 	{
+
+	    ChangeMe
+	 }
+EOF_TEST
+
+test_diff_funcname 'bash: subshell function' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT()
+EOF_HUNK
+RIGHT() (
+
+    ChangeMe=2
+)
+EOF_TEST
+
+test_diff_funcname 'bash: trailing comment' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT()
+EOF_HUNK
+RIGHT() { # Comment
+
+    ChangeMe
+}
+EOF_TEST
diff --git a/t/t4018/cpp-c++-function b/t/t4018/cpp-c++-function
deleted file mode 100644
index 9ee6bbef55..0000000000
--- a/t/t4018/cpp-c++-function
+++ /dev/null
@@ -1,4 +0,0 @@
-Item RIGHT::DoSomething( Args with_spaces )
-{
-	ChangeMe;
-}
diff --git a/t/t4018/cpp-c++-function.ctx b/t/t4018/cpp-c++-function.ctx
deleted file mode 100644
index 337b49dbb3..0000000000
--- a/t/t4018/cpp-c++-function.ctx
+++ /dev/null
@@ -1 +0,0 @@
-Item RIGHT::DoSomething( Args with_spaces )
diff --git a/t/t4018/cpp-class-constructor b/t/t4018/cpp-class-constructor
deleted file mode 100644
index ec4f115c25..0000000000
--- a/t/t4018/cpp-class-constructor
+++ /dev/null
@@ -1,4 +0,0 @@
-Item::Item(int RIGHT)
-{
-	ChangeMe;
-}
diff --git a/t/t4018/cpp-class-constructor-mem-init b/t/t4018/cpp-class-constructor-mem-init
deleted file mode 100644
index 49a69f37e1..0000000000
--- a/t/t4018/cpp-class-constructor-mem-init
+++ /dev/null
@@ -1,5 +0,0 @@
-Item::Item(int RIGHT) :
-	member(0)
-{
-	ChangeMe;
-}
diff --git a/t/t4018/cpp-class-constructor-mem-init.ctx b/t/t4018/cpp-class-constructor-mem-init.ctx
deleted file mode 100644
index 6664b8b3d8..0000000000
--- a/t/t4018/cpp-class-constructor-mem-init.ctx
+++ /dev/null
@@ -1 +0,0 @@
-Item::Item(int RIGHT) :
diff --git a/t/t4018/cpp-class-constructor.ctx b/t/t4018/cpp-class-constructor.ctx
deleted file mode 100644
index 2dcadfc0ba..0000000000
--- a/t/t4018/cpp-class-constructor.ctx
+++ /dev/null
@@ -1 +0,0 @@
-Item::Item(int RIGHT)
diff --git a/t/t4018/cpp-class-definition b/t/t4018/cpp-class-definition
deleted file mode 100644
index 11b61da3b7..0000000000
--- a/t/t4018/cpp-class-definition
+++ /dev/null
@@ -1,4 +0,0 @@
-class RIGHT
-{
-	int ChangeMe;
-};
diff --git a/t/t4018/cpp-class-definition-derived b/t/t4018/cpp-class-definition-derived
deleted file mode 100644
index 3b98cd09ab..0000000000
--- a/t/t4018/cpp-class-definition-derived
+++ /dev/null
@@ -1,5 +0,0 @@
-class RIGHT :
-	public Baseclass
-{
-	int ChangeMe;
-};
diff --git a/t/t4018/cpp-class-definition-derived.ctx b/t/t4018/cpp-class-definition-derived.ctx
deleted file mode 100644
index 146f0a7b7c..0000000000
--- a/t/t4018/cpp-class-definition-derived.ctx
+++ /dev/null
@@ -1 +0,0 @@
-class RIGHT :
diff --git a/t/t4018/cpp-class-definition.ctx b/t/t4018/cpp-class-definition.ctx
deleted file mode 100644
index 54bff816d6..0000000000
--- a/t/t4018/cpp-class-definition.ctx
+++ /dev/null
@@ -1 +0,0 @@
-class RIGHT
diff --git a/t/t4018/cpp-class-destructor b/t/t4018/cpp-class-destructor
deleted file mode 100644
index 5487665096..0000000000
--- a/t/t4018/cpp-class-destructor
+++ /dev/null
@@ -1,4 +0,0 @@
-RIGHT::~RIGHT()
-{
-	ChangeMe;
-}
diff --git a/t/t4018/cpp-class-destructor.ctx b/t/t4018/cpp-class-destructor.ctx
deleted file mode 100644
index 5390c17cf6..0000000000
--- a/t/t4018/cpp-class-destructor.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT::~RIGHT()
diff --git a/t/t4018/cpp-function-returning-global-type b/t/t4018/cpp-function-returning-global-type
deleted file mode 100644
index 1084d5990e..0000000000
--- a/t/t4018/cpp-function-returning-global-type
+++ /dev/null
@@ -1,4 +0,0 @@
-::Item get::it::RIGHT()
-{
-	ChangeMe;
-}
diff --git a/t/t4018/cpp-function-returning-global-type.ctx b/t/t4018/cpp-function-returning-global-type.ctx
deleted file mode 100644
index 4dcdde25f4..0000000000
--- a/t/t4018/cpp-function-returning-global-type.ctx
+++ /dev/null
@@ -1 +0,0 @@
-::Item get::it::RIGHT()
diff --git a/t/t4018/cpp-function-returning-nested b/t/t4018/cpp-function-returning-nested
deleted file mode 100644
index d9750aa61a..0000000000
--- a/t/t4018/cpp-function-returning-nested
+++ /dev/null
@@ -1,5 +0,0 @@
-get::Item get::it::RIGHT()
-{
-	ChangeMe;
-}
-
diff --git a/t/t4018/cpp-function-returning-nested.ctx b/t/t4018/cpp-function-returning-nested.ctx
deleted file mode 100644
index 6ef73c8368..0000000000
--- a/t/t4018/cpp-function-returning-nested.ctx
+++ /dev/null
@@ -1 +0,0 @@
-get::Item get::it::RIGHT()
diff --git a/t/t4018/cpp-function-returning-pointer b/t/t4018/cpp-function-returning-pointer
deleted file mode 100644
index ef15657ea8..0000000000
--- a/t/t4018/cpp-function-returning-pointer
+++ /dev/null
@@ -1,4 +0,0 @@
-const char *get_it_RIGHT(char *ptr)
-{
-	ChangeMe;
-}
diff --git a/t/t4018/cpp-function-returning-pointer.ctx b/t/t4018/cpp-function-returning-pointer.ctx
deleted file mode 100644
index bb0acce5c7..0000000000
--- a/t/t4018/cpp-function-returning-pointer.ctx
+++ /dev/null
@@ -1 +0,0 @@
-const char *get_it_RIGHT(char *ptr)
diff --git a/t/t4018/cpp-function-returning-reference b/t/t4018/cpp-function-returning-reference
deleted file mode 100644
index 01b051df70..0000000000
--- a/t/t4018/cpp-function-returning-reference
+++ /dev/null
@@ -1,4 +0,0 @@
-string& get::it::RIGHT(char *ptr)
-{
-	ChangeMe;
-}
diff --git a/t/t4018/cpp-function-returning-reference.ctx b/t/t4018/cpp-function-returning-reference.ctx
deleted file mode 100644
index 76afe381fd..0000000000
--- a/t/t4018/cpp-function-returning-reference.ctx
+++ /dev/null
@@ -1 +0,0 @@
-string& get::it::RIGHT(char *ptr)
diff --git a/t/t4018/cpp-gnu-style-function b/t/t4018/cpp-gnu-style-function
deleted file mode 100644
index 08c7c7565a..0000000000
--- a/t/t4018/cpp-gnu-style-function
+++ /dev/null
@@ -1,5 +0,0 @@
-const char *
-RIGHT(int arg)
-{
-	ChangeMe;
-}
diff --git a/t/t4018/cpp-gnu-style-function.ctx b/t/t4018/cpp-gnu-style-function.ctx
deleted file mode 100644
index 1858287812..0000000000
--- a/t/t4018/cpp-gnu-style-function.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT(int arg)
diff --git a/t/t4018/cpp-namespace-definition b/t/t4018/cpp-namespace-definition
deleted file mode 100644
index 6749980241..0000000000
--- a/t/t4018/cpp-namespace-definition
+++ /dev/null
@@ -1,4 +0,0 @@
-namespace RIGHT
-{
-	ChangeMe;
-}
diff --git a/t/t4018/cpp-namespace-definition.ctx b/t/t4018/cpp-namespace-definition.ctx
deleted file mode 100644
index 14c29c4638..0000000000
--- a/t/t4018/cpp-namespace-definition.ctx
+++ /dev/null
@@ -1 +0,0 @@
-namespace RIGHT
diff --git a/t/t4018/cpp-operator-definition b/t/t4018/cpp-operator-definition
deleted file mode 100644
index 1acd827159..0000000000
--- a/t/t4018/cpp-operator-definition
+++ /dev/null
@@ -1,4 +0,0 @@
-Value operator+(Value LEFT, Value RIGHT)
-{
-	ChangeMe;
-}
diff --git a/t/t4018/cpp-operator-definition.ctx b/t/t4018/cpp-operator-definition.ctx
deleted file mode 100644
index 5b56778961..0000000000
--- a/t/t4018/cpp-operator-definition.ctx
+++ /dev/null
@@ -1 +0,0 @@
-Value operator+(Value LEFT, Value RIGHT)
diff --git a/t/t4018/cpp-skip-access-specifiers b/t/t4018/cpp-skip-access-specifiers
deleted file mode 100644
index 4d4a9dbb9d..0000000000
--- a/t/t4018/cpp-skip-access-specifiers
+++ /dev/null
@@ -1,8 +0,0 @@
-class RIGHT : public Baseclass
-{
-public:
-protected:
-private:
-	void DoSomething();
-	int ChangeMe;
-};
diff --git a/t/t4018/cpp-skip-access-specifiers.ctx b/t/t4018/cpp-skip-access-specifiers.ctx
deleted file mode 100644
index 075bcd883b..0000000000
--- a/t/t4018/cpp-skip-access-specifiers.ctx
+++ /dev/null
@@ -1 +0,0 @@
-class RIGHT : public Baseclass
diff --git a/t/t4018/cpp-skip-comment-block b/t/t4018/cpp-skip-comment-block
deleted file mode 100644
index 3800b9967a..0000000000
--- a/t/t4018/cpp-skip-comment-block
+++ /dev/null
@@ -1,9 +0,0 @@
-struct item RIGHT(int i)
-// Do not
-// pick up
-/* these
-** comments.
-*/
-{
-	ChangeMe;
-}
diff --git a/t/t4018/cpp-skip-comment-block.ctx b/t/t4018/cpp-skip-comment-block.ctx
deleted file mode 100644
index 656c59893d..0000000000
--- a/t/t4018/cpp-skip-comment-block.ctx
+++ /dev/null
@@ -1 +0,0 @@
-struct item RIGHT(int i)
diff --git a/t/t4018/cpp-skip-labels b/t/t4018/cpp-skip-labels
deleted file mode 100644
index b9c10aba22..0000000000
--- a/t/t4018/cpp-skip-labels
+++ /dev/null
@@ -1,8 +0,0 @@
-void RIGHT (void)
-{
-repeat:		// C++ comment
-next:		/* C comment */
-	do_something();
-
-	ChangeMe;
-}
diff --git a/t/t4018/cpp-skip-labels.ctx b/t/t4018/cpp-skip-labels.ctx
deleted file mode 100644
index 6b0635f7f7..0000000000
--- a/t/t4018/cpp-skip-labels.ctx
+++ /dev/null
@@ -1 +0,0 @@
-void RIGHT (void)
diff --git a/t/t4018/cpp-struct-definition b/t/t4018/cpp-struct-definition
deleted file mode 100644
index 521c59fd15..0000000000
--- a/t/t4018/cpp-struct-definition
+++ /dev/null
@@ -1,9 +0,0 @@
-struct RIGHT {
-	unsigned
-	/* this bit field looks like a label and should not be picked up */
-		decoy_bitfield: 2,
-		more : 1;
-	int filler;
-
-	int ChangeMe;
-};
diff --git a/t/t4018/cpp-struct-definition.ctx b/t/t4018/cpp-struct-definition.ctx
deleted file mode 100644
index 48ed893279..0000000000
--- a/t/t4018/cpp-struct-definition.ctx
+++ /dev/null
@@ -1 +0,0 @@
-struct RIGHT {
diff --git a/t/t4018/cpp-struct-single-line b/t/t4018/cpp-struct-single-line
deleted file mode 100644
index a0de5fb800..0000000000
--- a/t/t4018/cpp-struct-single-line
+++ /dev/null
@@ -1,7 +0,0 @@
-void wrong()
-{
-}
-
-struct RIGHT_iterator_tag {};
-
-int ChangeMe;
diff --git a/t/t4018/cpp-struct-single-line.ctx b/t/t4018/cpp-struct-single-line.ctx
deleted file mode 100644
index e3bc9d5017..0000000000
--- a/t/t4018/cpp-struct-single-line.ctx
+++ /dev/null
@@ -1 +0,0 @@
-struct RIGHT_iterator_tag {};
diff --git a/t/t4018/cpp-template-function-definition b/t/t4018/cpp-template-function-definition
deleted file mode 100644
index 0cdf5ba5bd..0000000000
--- a/t/t4018/cpp-template-function-definition
+++ /dev/null
@@ -1,4 +0,0 @@
-template<class T> int RIGHT(T arg)
-{
-	ChangeMe;
-}
diff --git a/t/t4018/cpp-template-function-definition.ctx b/t/t4018/cpp-template-function-definition.ctx
deleted file mode 100644
index c9da39cf65..0000000000
--- a/t/t4018/cpp-template-function-definition.ctx
+++ /dev/null
@@ -1 +0,0 @@
-template<class T> int RIGHT(T arg)
diff --git a/t/t4018/cpp-union-definition b/t/t4018/cpp-union-definition
deleted file mode 100644
index 7ec94df697..0000000000
--- a/t/t4018/cpp-union-definition
+++ /dev/null
@@ -1,4 +0,0 @@
-union RIGHT {
-	double v;
-	int ChangeMe;
-};
diff --git a/t/t4018/cpp-union-definition.ctx b/t/t4018/cpp-union-definition.ctx
deleted file mode 100644
index 2fc7b54fb8..0000000000
--- a/t/t4018/cpp-union-definition.ctx
+++ /dev/null
@@ -1 +0,0 @@
-union RIGHT {
diff --git a/t/t4018/cpp-void-c-function b/t/t4018/cpp-void-c-function
deleted file mode 100644
index 153081e872..0000000000
--- a/t/t4018/cpp-void-c-function
+++ /dev/null
@@ -1,4 +0,0 @@
-void RIGHT (void)
-{
-	ChangeMe;
-}
diff --git a/t/t4018/cpp-void-c-function.ctx b/t/t4018/cpp-void-c-function.ctx
deleted file mode 100644
index 6b0635f7f7..0000000000
--- a/t/t4018/cpp-void-c-function.ctx
+++ /dev/null
@@ -1 +0,0 @@
-void RIGHT (void)
diff --git a/t/t4018/cpp.sh b/t/t4018/cpp.sh
new file mode 100755
index 0000000000..185d40d5ef
--- /dev/null
+++ b/t/t4018/cpp.sh
@@ -0,0 +1,239 @@
+#!/bin/sh
+#
+# See ../t4018-diff-funcname.sh's test_diff_funcname()
+#
+
+test_diff_funcname 'cpp: c++ function' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+Item RIGHT::DoSomething( Args with_spaces )
+EOF_HUNK
+Item RIGHT::DoSomething( Args with_spaces )
+{
+	ChangeMe;
+}
+EOF_TEST
+
+test_diff_funcname 'cpp: class constructor' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+Item::Item(int RIGHT)
+EOF_HUNK
+Item::Item(int RIGHT)
+{
+	ChangeMe;
+}
+EOF_TEST
+
+test_diff_funcname 'cpp: class constructor mem init' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+Item::Item(int RIGHT) :
+EOF_HUNK
+Item::Item(int RIGHT) :
+	member(0)
+{
+	ChangeMe;
+}
+EOF_TEST
+
+test_diff_funcname 'cpp: class definition' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+class RIGHT
+EOF_HUNK
+class RIGHT
+{
+	int ChangeMe;
+};
+EOF_TEST
+
+test_diff_funcname 'cpp: class definition derived' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+class RIGHT :
+EOF_HUNK
+class RIGHT :
+	public Baseclass
+{
+	int ChangeMe;
+};
+EOF_TEST
+
+test_diff_funcname 'cpp: class destructor' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT::~RIGHT()
+EOF_HUNK
+RIGHT::~RIGHT()
+{
+	ChangeMe;
+}
+EOF_TEST
+
+test_diff_funcname 'cpp: function returning global type' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+::Item get::it::RIGHT()
+EOF_HUNK
+::Item get::it::RIGHT()
+{
+	ChangeMe;
+}
+EOF_TEST
+
+test_diff_funcname 'cpp: function returning nested' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+get::Item get::it::RIGHT()
+EOF_HUNK
+get::Item get::it::RIGHT()
+{
+	ChangeMe;
+}
+
+EOF_TEST
+
+test_diff_funcname 'cpp: function returning pointer' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+const char *get_it_RIGHT(char *ptr)
+EOF_HUNK
+const char *get_it_RIGHT(char *ptr)
+{
+	ChangeMe;
+}
+EOF_TEST
+
+test_diff_funcname 'cpp: function returning reference' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+string& get::it::RIGHT(char *ptr)
+EOF_HUNK
+string& get::it::RIGHT(char *ptr)
+{
+	ChangeMe;
+}
+EOF_TEST
+
+test_diff_funcname 'cpp: gnu style function' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT(int arg)
+EOF_HUNK
+const char *
+RIGHT(int arg)
+{
+	ChangeMe;
+}
+EOF_TEST
+
+test_diff_funcname 'cpp: namespace definition' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+namespace RIGHT
+EOF_HUNK
+namespace RIGHT
+{
+	ChangeMe;
+}
+EOF_TEST
+
+test_diff_funcname 'cpp: operator definition' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+Value operator+(Value LEFT, Value RIGHT)
+EOF_HUNK
+Value operator+(Value LEFT, Value RIGHT)
+{
+	ChangeMe;
+}
+EOF_TEST
+
+test_diff_funcname 'cpp: skip access specifiers' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+class RIGHT : public Baseclass
+EOF_HUNK
+class RIGHT : public Baseclass
+{
+public:
+protected:
+private:
+	void DoSomething();
+	int ChangeMe;
+};
+EOF_TEST
+
+test_diff_funcname 'cpp: skip comment block' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+struct item RIGHT(int i)
+EOF_HUNK
+struct item RIGHT(int i)
+// Do not
+// pick up
+/* these
+** comments.
+*/
+{
+	ChangeMe;
+}
+EOF_TEST
+
+test_diff_funcname 'cpp: skip labels' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+void RIGHT (void)
+EOF_HUNK
+void RIGHT (void)
+{
+repeat:		// C++ comment
+next:		/* C comment */
+	do_something();
+
+	ChangeMe;
+}
+EOF_TEST
+
+test_diff_funcname 'cpp: struct definition' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+struct RIGHT {
+EOF_HUNK
+struct RIGHT {
+	unsigned
+	/* this bit field looks like a label and should not be picked up */
+		decoy_bitfield: 2,
+		more : 1;
+	int filler;
+
+	int ChangeMe;
+};
+EOF_TEST
+
+test_diff_funcname 'cpp: struct single line' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+struct RIGHT_iterator_tag {};
+EOF_HUNK
+void wrong()
+{
+}
+
+struct RIGHT_iterator_tag {};
+
+int ChangeMe;
+EOF_TEST
+
+test_diff_funcname 'cpp: template function definition' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+template<class T> int RIGHT(T arg)
+EOF_HUNK
+template<class T> int RIGHT(T arg)
+{
+	ChangeMe;
+}
+EOF_TEST
+
+test_diff_funcname 'cpp: union definition' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+union RIGHT {
+EOF_HUNK
+union RIGHT {
+	double v;
+	int ChangeMe;
+};
+EOF_TEST
+
+test_diff_funcname 'cpp: void c function' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+void RIGHT (void)
+EOF_HUNK
+void RIGHT (void)
+{
+	ChangeMe;
+}
+EOF_TEST
diff --git a/t/t4018/css-attribute-value-selector b/t/t4018/css-attribute-value-selector
deleted file mode 100644
index 918256b20c..0000000000
--- a/t/t4018/css-attribute-value-selector
+++ /dev/null
@@ -1,4 +0,0 @@
-[class*="RIGHT"] {
-    background : #000;
-    border : 10px ChangeMe #C6C6C6;
-}
diff --git a/t/t4018/css-attribute-value-selector.ctx b/t/t4018/css-attribute-value-selector.ctx
deleted file mode 100644
index 7f8956251c..0000000000
--- a/t/t4018/css-attribute-value-selector.ctx
+++ /dev/null
@@ -1 +0,0 @@
-[class*="RIGHT"] {
diff --git a/t/t4018/css-block-level-@-statements b/t/t4018/css-block-level-@-statements
deleted file mode 100644
index d6755f2f3d..0000000000
--- a/t/t4018/css-block-level-@-statements
+++ /dev/null
@@ -1,10 +0,0 @@
-@keyframes RIGHT {
-    from {
-        background : #000;
-        border : 10px ChangeMe #C6C6C6;
-    }
-    to {
-        background : #fff;
-        border : 10px solid #C6C6C6;
-    }
-}
diff --git a/t/t4018/css-block-level-@-statements.ctx b/t/t4018/css-block-level-@-statements.ctx
deleted file mode 100644
index 7f5e90468c..0000000000
--- a/t/t4018/css-block-level-@-statements.ctx
+++ /dev/null
@@ -1 +0,0 @@
-@keyframes RIGHT {
diff --git a/t/t4018/css-brace-in-col-1 b/t/t4018/css-brace-in-col-1
deleted file mode 100644
index 7831577506..0000000000
--- a/t/t4018/css-brace-in-col-1
+++ /dev/null
@@ -1,5 +0,0 @@
-RIGHT label.control-label
-{
-    margin-top: 10px!important;
-    border : 10px ChangeMe #C6C6C6;
-}
diff --git a/t/t4018/css-brace-in-col-1.ctx b/t/t4018/css-brace-in-col-1.ctx
deleted file mode 100644
index 91a9105c6a..0000000000
--- a/t/t4018/css-brace-in-col-1.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT label.control-label
diff --git a/t/t4018/css-class-selector b/t/t4018/css-class-selector
deleted file mode 100644
index f790a0062f..0000000000
--- a/t/t4018/css-class-selector
+++ /dev/null
@@ -1,4 +0,0 @@
-.RIGHT {
-    background : #000;
-    border : 10px ChangeMe #C6C6C6;
-}
diff --git a/t/t4018/css-class-selector.ctx b/t/t4018/css-class-selector.ctx
deleted file mode 100644
index ac7367d7f4..0000000000
--- a/t/t4018/css-class-selector.ctx
+++ /dev/null
@@ -1 +0,0 @@
-.RIGHT {
diff --git a/t/t4018/css-colon-eol b/t/t4018/css-colon-eol
deleted file mode 100644
index 5a30553d29..0000000000
--- a/t/t4018/css-colon-eol
+++ /dev/null
@@ -1,4 +0,0 @@
-RIGHT h1 {
-color:
-ChangeMe;
-}
diff --git a/t/t4018/css-colon-eol.ctx b/t/t4018/css-colon-eol.ctx
deleted file mode 100644
index b68493b9b0..0000000000
--- a/t/t4018/css-colon-eol.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT h1 {
diff --git a/t/t4018/css-colon-selector b/t/t4018/css-colon-selector
deleted file mode 100644
index c6d71fb42d..0000000000
--- a/t/t4018/css-colon-selector
+++ /dev/null
@@ -1,5 +0,0 @@
-RIGHT a:hover {
-    margin-top:
-    10px!important;
-    border : 10px ChangeMe #C6C6C6;
-}
diff --git a/t/t4018/css-colon-selector.ctx b/t/t4018/css-colon-selector.ctx
deleted file mode 100644
index 00b1a5aefe..0000000000
--- a/t/t4018/css-colon-selector.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT a:hover {
diff --git a/t/t4018/css-common b/t/t4018/css-common
deleted file mode 100644
index 84ed754b33..0000000000
--- a/t/t4018/css-common
+++ /dev/null
@@ -1,4 +0,0 @@
-RIGHT label.control-label {
-    margin-top: 10px!important;
-    border : 10px ChangeMe #C6C6C6;
-}
diff --git a/t/t4018/css-common.ctx b/t/t4018/css-common.ctx
deleted file mode 100644
index 43686b4081..0000000000
--- a/t/t4018/css-common.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT label.control-label {
diff --git a/t/t4018/css-id-selector b/t/t4018/css-id-selector
deleted file mode 100644
index 17c5111052..0000000000
--- a/t/t4018/css-id-selector
+++ /dev/null
@@ -1,4 +0,0 @@
-#RIGHT {
-    background : #000;
-    border : 10px ChangeMe #C6C6C6;
-}
diff --git a/t/t4018/css-id-selector.ctx b/t/t4018/css-id-selector.ctx
deleted file mode 100644
index ce19f6d8dc..0000000000
--- a/t/t4018/css-id-selector.ctx
+++ /dev/null
@@ -1 +0,0 @@
-#RIGHT {
diff --git a/t/t4018/css-long-selector-list b/t/t4018/css-long-selector-list
deleted file mode 100644
index 7ccd25d9ed..0000000000
--- a/t/t4018/css-long-selector-list
+++ /dev/null
@@ -1,6 +0,0 @@
-p.header,
-label.control-label,
-div ul#RIGHT {
-    margin-top: 10px!important;
-    border : 10px ChangeMe #C6C6C6;
-}
diff --git a/t/t4018/css-long-selector-list.ctx b/t/t4018/css-long-selector-list.ctx
deleted file mode 100644
index bc8d0fb62c..0000000000
--- a/t/t4018/css-long-selector-list.ctx
+++ /dev/null
@@ -1 +0,0 @@
-div ul#RIGHT {
diff --git a/t/t4018/css-prop-sans-indent b/t/t4018/css-prop-sans-indent
deleted file mode 100644
index a9e3c86b3c..0000000000
--- a/t/t4018/css-prop-sans-indent
+++ /dev/null
@@ -1,5 +0,0 @@
-RIGHT, label.control-label {
-margin-top: 10px!important;
-padding: 0;
-border : 10px ChangeMe #C6C6C6;
-}
diff --git a/t/t4018/css-prop-sans-indent.ctx b/t/t4018/css-prop-sans-indent.ctx
deleted file mode 100644
index cc880b2f44..0000000000
--- a/t/t4018/css-prop-sans-indent.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT, label.control-label {
diff --git a/t/t4018/css-root-selector b/t/t4018/css-root-selector
deleted file mode 100644
index 22b958e369..0000000000
--- a/t/t4018/css-root-selector
+++ /dev/null
@@ -1,4 +0,0 @@
-:RIGHT {
-    background : #000;
-    border : 10px ChangeMe #C6C6C6;
-}
diff --git a/t/t4018/css-root-selector.ctx b/t/t4018/css-root-selector.ctx
deleted file mode 100644
index 3010cded2a..0000000000
--- a/t/t4018/css-root-selector.ctx
+++ /dev/null
@@ -1 +0,0 @@
-:RIGHT {
diff --git a/t/t4018/css-short-selector-list b/t/t4018/css-short-selector-list
deleted file mode 100644
index 6a0bdee336..0000000000
--- a/t/t4018/css-short-selector-list
+++ /dev/null
@@ -1,4 +0,0 @@
-label.control, div ul#RIGHT {
-    margin-top: 10px!important;
-    border : 10px ChangeMe #C6C6C6;
-}
diff --git a/t/t4018/css-short-selector-list.ctx b/t/t4018/css-short-selector-list.ctx
deleted file mode 100644
index 9e5d87d126..0000000000
--- a/t/t4018/css-short-selector-list.ctx
+++ /dev/null
@@ -1 +0,0 @@
-label.control, div ul#RIGHT {
diff --git a/t/t4018/css-trailing-space b/t/t4018/css-trailing-space
deleted file mode 100644
index 32b5606c70..0000000000
--- a/t/t4018/css-trailing-space
+++ /dev/null
@@ -1,5 +0,0 @@
-RIGHT label.control-label {
-    margin:10px;   
-    padding:10px;
-    border : 10px ChangeMe #C6C6C6;
-}
diff --git a/t/t4018/css-trailing-space.ctx b/t/t4018/css-trailing-space.ctx
deleted file mode 100644
index 43686b4081..0000000000
--- a/t/t4018/css-trailing-space.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT label.control-label {
diff --git a/t/t4018/css.sh b/t/t4018/css.sh
new file mode 100755
index 0000000000..106a3de242
--- /dev/null
+++ b/t/t4018/css.sh
@@ -0,0 +1,146 @@
+#!/bin/sh
+#
+# See ../t4018-diff-funcname.sh's test_diff_funcname()
+#
+
+test_diff_funcname 'css: attribute value selector' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+[class*="RIGHT"] {
+EOF_HUNK
+[class*="RIGHT"] {
+    background : #000;
+    border : 10px ChangeMe #C6C6C6;
+}
+EOF_TEST
+
+test_diff_funcname 'css: block level @ statements' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+@keyframes RIGHT {
+EOF_HUNK
+@keyframes RIGHT {
+    from {
+        background : #000;
+        border : 10px ChangeMe #C6C6C6;
+    }
+    to {
+        background : #fff;
+        border : 10px solid #C6C6C6;
+    }
+}
+EOF_TEST
+
+test_diff_funcname 'css: brace in col 1' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT label.control-label
+EOF_HUNK
+RIGHT label.control-label
+{
+    margin-top: 10px!important;
+    border : 10px ChangeMe #C6C6C6;
+}
+EOF_TEST
+
+test_diff_funcname 'css: class selector' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+.RIGHT {
+EOF_HUNK
+.RIGHT {
+    background : #000;
+    border : 10px ChangeMe #C6C6C6;
+}
+EOF_TEST
+
+test_diff_funcname 'css: colon eol' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT h1 {
+EOF_HUNK
+RIGHT h1 {
+color:
+ChangeMe;
+}
+EOF_TEST
+
+test_diff_funcname 'css: colon selector' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT a:hover {
+EOF_HUNK
+RIGHT a:hover {
+    margin-top:
+    10px!important;
+    border : 10px ChangeMe #C6C6C6;
+}
+EOF_TEST
+
+test_diff_funcname 'css: common' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT label.control-label {
+EOF_HUNK
+RIGHT label.control-label {
+    margin-top: 10px!important;
+    border : 10px ChangeMe #C6C6C6;
+}
+EOF_TEST
+
+test_diff_funcname 'css: id selector' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+#RIGHT {
+EOF_HUNK
+#RIGHT {
+    background : #000;
+    border : 10px ChangeMe #C6C6C6;
+}
+EOF_TEST
+
+test_diff_funcname 'css: long selector list' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+div ul#RIGHT {
+EOF_HUNK
+p.header,
+label.control-label,
+div ul#RIGHT {
+    margin-top: 10px!important;
+    border : 10px ChangeMe #C6C6C6;
+}
+EOF_TEST
+
+test_diff_funcname 'css: prop sans indent' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT, label.control-label {
+EOF_HUNK
+RIGHT, label.control-label {
+margin-top: 10px!important;
+padding: 0;
+border : 10px ChangeMe #C6C6C6;
+}
+EOF_TEST
+
+test_diff_funcname 'css: root selector' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+:RIGHT {
+EOF_HUNK
+:RIGHT {
+    background : #000;
+    border : 10px ChangeMe #C6C6C6;
+}
+EOF_TEST
+
+test_diff_funcname 'css: short selector list' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+label.control, div ul#RIGHT {
+EOF_HUNK
+label.control, div ul#RIGHT {
+    margin-top: 10px!important;
+    border : 10px ChangeMe #C6C6C6;
+}
+EOF_TEST
+
+test_diff_funcname 'css: trailing space' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT label.control-label {
+EOF_HUNK
+RIGHT label.control-label {
+    margin:10px;   
+    padding:10px;
+    border : 10px ChangeMe #C6C6C6;
+}
+EOF_TEST
diff --git a/t/t4018/custom1-pattern.ctx b/t/t4018/custom1-pattern.ctx
deleted file mode 100644
index d1609cc9a6..0000000000
--- a/t/t4018/custom1-pattern.ctx
+++ /dev/null
@@ -1 +0,0 @@
-int special, RIGHT;
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 e8fd59f884..f8bbccadb4
--- a/t/t4018/custom1-pattern
+++ b/t/t4018/custom1.sh
@@ -1,3 +1,12 @@
+#!/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;
@@ -15,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 f88ac318b7..0000000000
--- a/t/t4018/custom2-match-to-end-of-line
+++ /dev/null
@@ -1,8 +0,0 @@
-public class RIGHT_Beer
-{
-	int special;
-	public static void main(String args[])
-	{
-		System.out.print("ChangeMe");
-	}
-}
diff --git a/t/t4018/custom2-match-to-end-of-line.ctx b/t/t4018/custom2-match-to-end-of-line.ctx
deleted file mode 100644
index 8294c6e49b..0000000000
--- a/t/t4018/custom2-match-to-end-of-line.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT_Beer
diff --git a/t/t4018/custom2.sh b/t/t4018/custom2.sh
new file mode 100755
index 0000000000..c68421f788
--- /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.ctx b/t/t4018/custom3-alternation-in-pattern.ctx
deleted file mode 100644
index 2125474b68..0000000000
--- a/t/t4018/custom3-alternation-in-pattern.ctx
+++ /dev/null
@@ -1 +0,0 @@
-public static void main(String RIGHT[])
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 5f3769c64f..07c5c134ff
--- a/t/t4018/custom3-alternation-in-pattern
+++ b/t/t4018/custom3.sh
@@ -1,3 +1,12 @@
+#!/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;
@@ -15,3 +24,4 @@ public class Beer
 			+ "99 bottles of beer on the wall.\n");
 	}
 }
+EOF_TEST
diff --git a/t/t4018/dts-labels b/t/t4018/dts-labels
deleted file mode 100644
index b21ef8737b..0000000000
--- a/t/t4018/dts-labels
+++ /dev/null
@@ -1,9 +0,0 @@
-/ {
-	label_1: node1@ff00 {
-		label2: RIGHT {
-			vendor,some-property;
-
-			ChangeMe = <0x45-30>;
-		};
-	};
-};
diff --git a/t/t4018/dts-labels.ctx b/t/t4018/dts-labels.ctx
deleted file mode 100644
index 48d9373cab..0000000000
--- a/t/t4018/dts-labels.ctx
+++ /dev/null
@@ -1 +0,0 @@
-label2: RIGHT {
diff --git a/t/t4018/dts-node-unitless b/t/t4018/dts-node-unitless
deleted file mode 100644
index c5287d9141..0000000000
--- a/t/t4018/dts-node-unitless
+++ /dev/null
@@ -1,8 +0,0 @@
-/ {
-	label_1: node1 {
-		RIGHT {
-			prop-array = <1>, <4>;
-			ChangeMe = <0xffeedd00>;
-		};
-	};
-};
diff --git a/t/t4018/dts-node-unitless.ctx b/t/t4018/dts-node-unitless.ctx
deleted file mode 100644
index 82c8683fa1..0000000000
--- a/t/t4018/dts-node-unitless.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT {
diff --git a/t/t4018/dts-nodes b/t/t4018/dts-nodes
deleted file mode 100644
index 5a4334bb16..0000000000
--- a/t/t4018/dts-nodes
+++ /dev/null
@@ -1,8 +0,0 @@
-/ {
-	label_1: node1@ff00 {
-		RIGHT@deadf00,4000 {
-			#size-cells = <1>;
-			ChangeMe = <0xffeedd00>;
-		};
-	};
-};
diff --git a/t/t4018/dts-nodes-boolean-prop b/t/t4018/dts-nodes-boolean-prop
deleted file mode 100644
index afc6b5b404..0000000000
--- a/t/t4018/dts-nodes-boolean-prop
+++ /dev/null
@@ -1,9 +0,0 @@
-/ {
-	label_1: node1@ff00 {
-		RIGHT@deadf00,4000 {
-			boolean-prop1;
-
-			ChangeMe;
-		};
-	};
-};
diff --git a/t/t4018/dts-nodes-boolean-prop.ctx b/t/t4018/dts-nodes-boolean-prop.ctx
deleted file mode 100644
index 3a0232d55d..0000000000
--- a/t/t4018/dts-nodes-boolean-prop.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT@deadf00,4000 {
diff --git a/t/t4018/dts-nodes-comment1 b/t/t4018/dts-nodes-comment1
deleted file mode 100644
index 559dfce9b3..0000000000
--- a/t/t4018/dts-nodes-comment1
+++ /dev/null
@@ -1,8 +0,0 @@
-/ {
-	label_1: node1@ff00 {
-		RIGHT@deadf00,4000 /* &a comment */ {
-			#size-cells = <1>;
-			ChangeMe = <0xffeedd00>;
-		};
-	};
-};
diff --git a/t/t4018/dts-nodes-comment1.ctx b/t/t4018/dts-nodes-comment1.ctx
deleted file mode 100644
index ec364600b1..0000000000
--- a/t/t4018/dts-nodes-comment1.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT@deadf00,4000 /* &a comment */ {
diff --git a/t/t4018/dts-nodes-comment2 b/t/t4018/dts-nodes-comment2
deleted file mode 100644
index 27e9718b31..0000000000
--- a/t/t4018/dts-nodes-comment2
+++ /dev/null
@@ -1,8 +0,0 @@
-/ {
-	label_1: node1@ff00 {
-		RIGHT@deadf00,4000 { /* a trailing comment */ 
-			#size-cells = <1>;
-			ChangeMe = <0xffeedd00>;
-		};
-	};
-};
diff --git a/t/t4018/dts-nodes-comment2.ctx b/t/t4018/dts-nodes-comment2.ctx
deleted file mode 100644
index 75f0d75258..0000000000
--- a/t/t4018/dts-nodes-comment2.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT@deadf00,4000 { /* a trailing comment */
diff --git a/t/t4018/dts-nodes-multiline-prop b/t/t4018/dts-nodes-multiline-prop
deleted file mode 100644
index 072d58b69d..0000000000
--- a/t/t4018/dts-nodes-multiline-prop
+++ /dev/null
@@ -1,13 +0,0 @@
-/ {
-	label_1: node1@ff00 {
-		RIGHT@deadf00,4000 {
-			multilineprop = <3>,
-					<4>,
-					<5>,
-					<6>,
-					<7>;
-
-			ChangeMe = <0xffeedd00>;
-		};
-	};
-};
diff --git a/t/t4018/dts-nodes-multiline-prop.ctx b/t/t4018/dts-nodes-multiline-prop.ctx
deleted file mode 100644
index 3a0232d55d..0000000000
--- a/t/t4018/dts-nodes-multiline-prop.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT@deadf00,4000 {
diff --git a/t/t4018/dts-nodes.ctx b/t/t4018/dts-nodes.ctx
deleted file mode 100644
index 3a0232d55d..0000000000
--- a/t/t4018/dts-nodes.ctx
+++ /dev/null
@@ -1 +0,0 @@
-RIGHT@deadf00,4000 {
diff --git a/t/t4018/dts-reference b/t/t4018/dts-reference
deleted file mode 100644
index 8f0c87d863..0000000000
--- a/t/t4018/dts-reference
+++ /dev/null
@@ -1,9 +0,0 @@
-&label_1 {
-	TEST = <455>;
-};
-
-&RIGHT {
-	vendor,some-property;
-
-	ChangeMe = <0x45-30>;
-};
diff --git a/t/t4018/dts-reference.ctx b/t/t4018/dts-reference.ctx
deleted file mode 100644
index c1e13409ee..0000000000
--- a/t/t4018/dts-reference.ctx
+++ /dev/null
@@ -1 +0,0 @@
-&RIGHT {
diff --git a/t/t4018/dts-root b/t/t4018/dts-root
deleted file mode 100644
index 4353b8220c..0000000000
--- a/t/t4018/dts-root
+++ /dev/null
@@ -1,5 +0,0 @@
-/ { RIGHT /* Technically just supposed to be a slash and brace */
-	#size-cells = <1>;
-
-	ChangeMe = <0xffeedd00>;
-};
diff --git a/t/t4018/dts-root-comment b/t/t4018/dts-root-comment
deleted file mode 100644
index 333a625c70..0000000000
--- a/t/t4018/dts-root-comment
+++ /dev/null
@@ -1,8 +0,0 @@
-/ { RIGHT /* Technically just supposed to be a slash and brace */
-	#size-cells = <1>;
-
-	/* This comment should be ignored */
-
-	some-property = <40+2>;
-	ChangeMe = <0xffeedd00>;
-};
diff --git a/t/t4018/dts-root-comment.ctx b/t/t4018/dts-root-comment.ctx
deleted file mode 100644
index 656053dd42..0000000000
--- a/t/t4018/dts-root-comment.ctx
+++ /dev/null
@@ -1 +0,0 @@
-/ { RIGHT /* Technically just supposed to be a slash and brace */
diff --git a/t/t4018/dts-root.ctx b/t/t4018/dts-root.ctx
deleted file mode 100644
index 656053dd42..0000000000
--- a/t/t4018/dts-root.ctx
+++ /dev/null
@@ -1 +0,0 @@
-/ { RIGHT /* Technically just supposed to be a slash and brace */
diff --git a/t/t4018/dts.sh b/t/t4018/dts.sh
new file mode 100755
index 0000000000..304c131d86
--- /dev/null
+++ b/t/t4018/dts.sh
@@ -0,0 +1,149 @@
+#!/bin/sh
+#
+# See ../t4018-diff-funcname.sh's test_diff_funcname()
+#
+
+test_diff_funcname 'dts: labels' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+label2: RIGHT {
+EOF_HUNK
+/ {
+	label_1: node1@ff00 {
+		label2: RIGHT {
+			vendor,some-property;
+
+			ChangeMe = <0x45-30>;
+		};
+	};
+};
+EOF_TEST
+
+test_diff_funcname 'dts: node unitless' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT {
+EOF_HUNK
+/ {
+	label_1: node1 {
+		RIGHT {
+			prop-array = <1>, <4>;
+			ChangeMe = <0xffeedd00>;
+		};
+	};
+};
+EOF_TEST
+
+test_diff_funcname 'dts: nodes' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT@deadf00,4000 {
+EOF_HUNK
+/ {
+	label_1: node1@ff00 {
+		RIGHT@deadf00,4000 {
+			#size-cells = <1>;
+			ChangeMe = <0xffeedd00>;
+		};
+	};
+};
+EOF_TEST
+
+test_diff_funcname 'dts: nodes boolean prop' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT@deadf00,4000 {
+EOF_HUNK
+/ {
+	label_1: node1@ff00 {
+		RIGHT@deadf00,4000 {
+			boolean-prop1;
+
+			ChangeMe;
+		};
+	};
+};
+EOF_TEST
+
+test_diff_funcname 'dts: nodes comment1' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT@deadf00,4000 /* &a comment */ {
+EOF_HUNK
+/ {
+	label_1: node1@ff00 {
+		RIGHT@deadf00,4000 /* &a comment */ {
+			#size-cells = <1>;
+			ChangeMe = <0xffeedd00>;
+		};
+	};
+};
+EOF_TEST
+
+test_diff_funcname 'dts: nodes comment2' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT@deadf00,4000 { /* a trailing comment */
+EOF_HUNK
+/ {
+	label_1: node1@ff00 {
+		RIGHT@deadf00,4000 { /* a trailing comment */ 
+			#size-cells = <1>;
+			ChangeMe = <0xffeedd00>;
+		};
+	};
+};
+EOF_TEST
+
+test_diff_funcname 'dts: nodes multiline prop' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+RIGHT@deadf00,4000 {
+EOF_HUNK
+/ {
+	label_1: node1@ff00 {
+		RIGHT@deadf00,4000 {
+			multilineprop = <3>,
+					<4>,
+					<5>,
+					<6>,
+					<7>;
+
+			ChangeMe = <0xffeedd00>;
+		};
+	};
+};
+EOF_TEST
+
+test_diff_funcname 'dts: reference' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+&RIGHT {
+EOF_HUNK
+&label_1 {
+	TEST = <455>;
+};
+
+&RIGHT {
+	vendor,some-property;
+
+	ChangeMe = <0x45-30>;
+};
+EOF_TEST
+
+test_diff_funcname 'dts: root' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+/ { RIGHT /* Technically just supposed to be a slash and brace */
+EOF_HUNK
+/ { RIGHT /* Technically just supposed to be a slash and brace */
+	#size-cells = <1>;
+
+	ChangeMe = <0xffeedd00>;
+};
+EOF_TEST
+
+test_diff_funcname 'dts: root comment' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+/ { RIGHT /* Technically just supposed to be a slash and brace */
+EOF_HUNK
+/ { RIGHT /* Technically just supposed to be a slash and brace */
+	#size-cells = <1>;
+
+	/* This comment should be ignored */
+
+	some-property = <40+2>;
+	ChangeMe = <0xffeedd00>;
+};
+EOF_TEST
diff --git a/t/t4018/elixir-do-not-pick-end b/t/t4018/elixir-do-not-pick-end
deleted file mode 100644
index fae08ba7e8..0000000000
--- a/t/t4018/elixir-do-not-pick-end
+++ /dev/null
@@ -1,5 +0,0 @@
-defmodule RIGHT do
-end
-#
-#
-# ChangeMe; do not pick up 'end' line
diff --git a/t/t4018/elixir-do-not-pick-end.ctx b/t/t4018/elixir-do-not-pick-end.ctx
deleted file mode 100644
index 8f28a7a689..0000000000
--- a/t/t4018/elixir-do-not-pick-end.ctx
+++ /dev/null
@@ -1 +0,0 @@
-defmodule RIGHT do
diff --git a/t/t4018/elixir-ex-unit-test b/t/t4018/elixir-ex-unit-test
deleted file mode 100644
index 0560a2b697..0000000000
--- a/t/t4018/elixir-ex-unit-test
+++ /dev/null
@@ -1,6 +0,0 @@
-defmodule Test do
-  test "RIGHT" do
-    assert true == true
-    assert ChangeMe
-  end
-end
diff --git a/t/t4018/elixir-ex-unit-test.ctx b/t/t4018/elixir-ex-unit-test.ctx
deleted file mode 100644
index a55e3de2cc..0000000000
--- a/t/t4018/elixir-ex-unit-test.ctx
+++ /dev/null
@@ -1 +0,0 @@
-test "RIGHT" do
diff --git a/t/t4018/elixir-function b/t/t4018/elixir-function
deleted file mode 100644
index d452f495a7..0000000000
--- a/t/t4018/elixir-function
+++ /dev/null
@@ -1,5 +0,0 @@
-def function(RIGHT, arg) do
-  # comment
-  # comment
-  ChangeMe
-end
diff --git a/t/t4018/elixir-function.ctx b/t/t4018/elixir-function.ctx
deleted file mode 100644
index 62aee9c8b1..0000000000
--- a/t/t4018/elixir-function.ctx
+++ /dev/null
@@ -1 +0,0 @@
-def function(RIGHT, arg) do
diff --git a/t/t4018/elixir-macro b/t/t4018/elixir-macro
deleted file mode 100644
index 4f925e9ad4..0000000000
--- a/t/t4018/elixir-macro
+++ /dev/null
@@ -1,5 +0,0 @@
-defmacro foo(RIGHT) do
-  # Code
-  # Code
-  ChangeMe
-end
diff --git a/t/t4018/elixir-macro.ctx b/t/t4018/elixir-macro.ctx
deleted file mode 100644
index fc1d3b85e8..0000000000
--- a/t/t4018/elixir-macro.ctx
+++ /dev/null
@@ -1 +0,0 @@
-defmacro foo(RIGHT) do
diff --git a/t/t4018/elixir-module b/t/t4018/elixir-module
deleted file mode 100644
index 91a4e7aa20..0000000000
--- a/t/t4018/elixir-module
+++ /dev/null
@@ -1,9 +0,0 @@
-defmodule RIGHT do
-  @moduledoc """
-  Foo bar
-  """
-
-  def ChangeMe(a) where is_map(a) do
-    a
-  end
-end
diff --git a/t/t4018/elixir-module-func b/t/t4018/elixir-module-func
deleted file mode 100644
index c9910d0675..0000000000
--- a/t/t4018/elixir-module-func
+++ /dev/null
@@ -1,8 +0,0 @@
-defmodule Foo do
-  def fun(RIGHT) do
-     # Code
-     # Code
-     # Code
-     ChangeMe
-  end
-end
diff --git a/t/t4018/elixir-module-func.ctx b/t/t4018/elixir-module-func.ctx
deleted file mode 100644
index 8239214386..0000000000
--- a/t/t4018/elixir-module-func.ctx
+++ /dev/null
@@ -1 +0,0 @@
-def fun(RIGHT) do
diff --git a/t/t4018/elixir-module.ctx b/t/t4018/elixir-module.ctx
deleted file mode 100644
index 8f28a7a689..0000000000
--- a/t/t4018/elixir-module.ctx
+++ /dev/null
@@ -1 +0,0 @@
-defmodule RIGHT do
diff --git a/t/t4018/elixir-nested-module b/t/t4018/elixir-nested-module
deleted file mode 100644
index 771ebc5c42..0000000000
--- a/t/t4018/elixir-nested-module
+++ /dev/null
@@ -1,9 +0,0 @@
-defmodule MyApp.RIGHT do
-  @moduledoc """
-  Foo bar
-  """
-
-  def ChangeMe(a) where is_map(a) do
-    a
-  end
-end
diff --git a/t/t4018/elixir-nested-module.ctx b/t/t4018/elixir-nested-module.ctx
deleted file mode 100644
index 3ffbdd18b1..0000000000
--- a/t/t4018/elixir-nested-module.ctx
+++ /dev/null
@@ -1 +0,0 @@
-defmodule MyApp.RIGHT do
diff --git a/t/t4018/elixir-private-function b/t/t4018/elixir-private-function
deleted file mode 100644
index 1aabe33b7a..0000000000
--- a/t/t4018/elixir-private-function
+++ /dev/null
@@ -1,5 +0,0 @@
-defp function(RIGHT, arg) do
-  # comment
-  # comment
-  ChangeMe
-end
diff --git a/t/t4018/elixir-private-function.ctx b/t/t4018/elixir-private-function.ctx
deleted file mode 100644
index 1c4eba44f7..0000000000
--- a/t/t4018/elixir-private-function.ctx
+++ /dev/null
@@ -1 +0,0 @@
-defp function(RIGHT, arg) do
diff --git a/t/t4018/elixir-protocol b/t/t4018/elixir-protocol
deleted file mode 100644
index 7d9173691e..0000000000
--- a/t/t4018/elixir-protocol
+++ /dev/null
@@ -1,6 +0,0 @@
-defprotocol RIGHT do
-  @doc """
-  Calculates the size (and not the length!) of a data structure
-  """
-  def size(data, ChangeMe)
-end
diff --git a/t/t4018/elixir-protocol-implementation b/t/t4018/elixir-protocol-implementation
deleted file mode 100644
index f9234bbfc4..0000000000
--- a/t/t4018/elixir-protocol-implementation
+++ /dev/null
@@ -1,5 +0,0 @@
-defimpl RIGHT do
-  # Docs
-  # Docs
-  def foo(ChangeMe), do: :ok
-end
diff --git a/t/t4018/elixir-protocol-implementation.ctx b/t/t4018/elixir-protocol-implementation.ctx
deleted file mode 100644
index efb758aea6..0000000000
--- a/t/t4018/elixir-protocol-implementation.ctx
+++ /dev/null
@@ -1 +0,0 @@
-defimpl RIGHT do
diff --git a/t/t4018/elixir-protocol.ctx b/t/t4018/elixir-protocol.ctx
deleted file mode 100644
index d0204e9f14..0000000000
--- a/t/t4018/elixir-protocol.ctx
+++ /dev/null
@@ -1 +0,0 @@
-defprotocol RIGHT do
diff --git a/t/t4018/elixir.sh b/t/t4018/elixir.sh
new file mode 100755
index 0000000000..6b09df9520
--- /dev/null
+++ b/t/t4018/elixir.sh
@@ -0,0 +1,127 @@
+#!/bin/sh
+#
+# See ../t4018-diff-funcname.sh's test_diff_funcname()
+#
+
+test_diff_funcname 'elixir: do not pick end' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+defmodule RIGHT do
+EOF_HUNK
+defmodule RIGHT do
+end
+#
+#
+# ChangeMe; do not pick up 'end' line
+EOF_TEST
+
+test_diff_funcname 'elixir: ex unit test' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+test "RIGHT" do
+EOF_HUNK
+defmodule Test do
+  test "RIGHT" do
+    assert true == true
+    assert ChangeMe
+  end
+end
+EOF_TEST
+
+test_diff_funcname 'elixir: function' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+def function(RIGHT, arg) do
+EOF_HUNK
+def function(RIGHT, arg) do
+  # comment
+  # comment
+  ChangeMe
+end
+EOF_TEST
+
+test_diff_funcname 'elixir: macro' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+defmacro foo(RIGHT) do
+EOF_HUNK
+defmacro foo(RIGHT) do
+  # Code
+  # Code
+  ChangeMe
+end
+EOF_TEST
+
+test_diff_funcname 'elixir: module' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+defmodule RIGHT do
+EOF_HUNK
+defmodule RIGHT do
+  @moduledoc """
+  Foo bar
+  """
+
+  def ChangeMe(a) where is_map(a) do
+    a
+  end
+end
+EOF_TEST
+
+test_diff_funcname 'elixir: module func' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+def fun(RIGHT) do
+EOF_HUNK
+defmodule Foo do
+  def fun(RIGHT) do
+     # Code
+     # Code
+     # Code
+     ChangeMe
+  end
+end
+EOF_TEST
+
+test_diff_funcname 'elixir: nested module' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+defmodule MyApp.RIGHT do
+EOF_HUNK
+defmodule MyApp.RIGHT do
+  @moduledoc """
+  Foo bar
+  """
+
+  def ChangeMe(a) where is_map(a) do
+    a
+  end
+end
+EOF_TEST
+
+test_diff_funcname 'elixir: private function' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+defp function(RIGHT, arg) do
+EOF_HUNK
+defp function(RIGHT, arg) do
+  # comment
+  # comment
+  ChangeMe
+end
+EOF_TEST
+
+test_diff_funcname 'elixir: protocol' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+defprotocol RIGHT do
+EOF_HUNK
+defprotocol RIGHT do
+  @doc """
+  Calculates the size (and not the length!) of a data structure
+  """
+  def size(data, ChangeMe)
+end
+EOF_TEST
+
+test_diff_funcname 'elixir: protocol implementation' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+defimpl RIGHT do
+EOF_HUNK
+defimpl RIGHT do
+  # Docs
+  # Docs
+  def foo(ChangeMe), do: :ok
+end
+EOF_TEST
diff --git a/t/t4018/fortran-block-data b/t/t4018/fortran-block-data
deleted file mode 100644
index 63d4e21d0a..0000000000
--- a/t/t4018/fortran-block-data
+++ /dev/null
@@ -1,5 +0,0 @@
-       BLOCK DATA RIGHT
-       
-       COMMON /B/ C, ChangeMe
-       DATA C, ChangeMe  / 2.0, 6.0 / 
-       END 
diff --git a/t/t4018/fortran-block-data.ctx b/t/t4018/fortran-block-data.ctx
deleted file mode 100644
index c3db084ccc..0000000000
--- a/t/t4018/fortran-block-data.ctx
+++ /dev/null
@@ -1 +0,0 @@
-BLOCK DATA RIGHT
diff --git a/t/t4018/fortran-comment b/t/t4018/fortran-comment
deleted file mode 100644
index 7b10d17658..0000000000
--- a/t/t4018/fortran-comment
+++ /dev/null
@@ -1,13 +0,0 @@
-      module a
-
-      contains
-
-      ! subroutine wrong
-      subroutine RIGHT
-      ! subroutine wrong
-
-      real ChangeMe
-
-      end subroutine RIGHT
-
-      end module a
diff --git a/t/t4018/fortran-comment-keyword b/t/t4018/fortran-comment-keyword
deleted file mode 100644
index e9206a5379..0000000000
--- a/t/t4018/fortran-comment-keyword
+++ /dev/null
@@ -1,14 +0,0 @@
-      module a
-
-      contains
-
-      subroutine RIGHT (funcA, funcB)
-
-      real funcA  ! grid function a
-      real funcB  ! grid function b
-
-      real ChangeMe
-
-      end subroutine RIGHT
-
-      end module a
diff --git a/t/t4018/fortran-comment-keyword.ctx b/t/t4018/fortran-comment-keyword.ctx
deleted file mode 100644
index 0b9220b355..0000000000
--- a/t/t4018/fortran-comment-keyword.ctx
+++ /dev/null
@@ -1 +0,0 @@
-subroutine RIGHT (funcA, funcB)
diff --git a/t/t4018/fortran-comment-legacy b/t/t4018/fortran-comment-legacy
deleted file mode 100644
index 53cd062c1e..0000000000
--- a/t/t4018/fortran-comment-legacy
+++ /dev/null
@@ -1,13 +0,0 @@
-      module a
-
-      contains
-
-C subroutine wrong
-      subroutine RIGHT
-C subroutine wrong
-
-      real ChangeMe
-
-      end subroutine RIGHT
-
-      end module a
diff --git a/t/t4018/fortran-comment-legacy-star b/t/t4018/fortran-comment-legacy-star
deleted file mode 100644
index 2cbcdc3d8a..0000000000
--- a/t/t4018/fortran-comment-legacy-star
+++ /dev/null
@@ -1,13 +0,0 @@
-      module a
-
-      contains
-
-* subroutine wrong
-      subroutine RIGHT
-* subroutine wrong
-
-      real ChangeMe
-
-      end subroutine RIGHT
-
-      end module a
diff --git a/t/t4018/fortran-comment-legacy-star.ctx b/t/t4018/fortran-comment-legacy-star.ctx
deleted file mode 100644
index 6a34203f80..0000000000
--- a/t/t4018/fortran-comment-legacy-star.ctx
+++ /dev/null
@@ -1 +0,0 @@
-subroutine RIGHT
diff --git a/t/t4018/fortran-comment-legacy.ctx b/t/t4018/fortran-comment-legacy.ctx
deleted file mode 100644
index 6a34203f80..0000000000
--- a/t/t4018/fortran-comment-legacy.ctx
+++ /dev/null
@@ -1 +0,0 @@
-subroutine RIGHT
diff --git a/t/t4018/fortran-comment.ctx b/t/t4018/fortran-comment.ctx
deleted file mode 100644
index 6a34203f80..0000000000
--- a/t/t4018/fortran-comment.ctx
+++ /dev/null
@@ -1 +0,0 @@
-subroutine RIGHT
diff --git a/t/t4018/fortran-external-function b/t/t4018/fortran-external-function
deleted file mode 100644
index 5a2d85d3aa..0000000000
--- a/t/t4018/fortran-external-function
+++ /dev/null
@@ -1,9 +0,0 @@
-function RIGHT(a, b) result(c)
-
-integer, intent(in) :: ChangeMe
-integer, intent(in) :: b
-integer, intent(out) :: c
-
-c = a+b
-
-end function RIGHT
diff --git a/t/t4018/fortran-external-function.ctx b/t/t4018/fortran-external-function.ctx
deleted file mode 100644
index 56ec4d8eca..0000000000
--- a/t/t4018/fortran-external-function.ctx
+++ /dev/null
@@ -1 +0,0 @@
-function RIGHT(a, b) result(c)
diff --git a/t/t4018/fortran-external-subroutine b/t/t4018/fortran-external-subroutine
deleted file mode 100644
index 4ce85fea13..0000000000
--- a/t/t4018/fortran-external-subroutine
+++ /dev/null
@@ -1,5 +0,0 @@
-subroutine RIGHT
-
-real ChangeMe
-
-end subroutine RIGHT
diff --git a/t/t4018/fortran-external-subroutine.ctx b/t/t4018/fortran-external-subroutine.ctx
deleted file mode 100644
index 6a34203f80..0000000000
--- a/t/t4018/fortran-external-subroutine.ctx
+++ /dev/null
@@ -1 +0,0 @@
-subroutine RIGHT
diff --git a/t/t4018/fortran-module b/t/t4018/fortran-module
deleted file mode 100644
index c4b737dac3..0000000000
--- a/t/t4018/fortran-module
+++ /dev/null
@@ -1,5 +0,0 @@
-module RIGHT
-
-use ChangeMe
-
-end module RIGHT
diff --git a/t/t4018/fortran-module-procedure b/t/t4018/fortran-module-procedure
deleted file mode 100644
index 1ce6d854c2..0000000000
--- a/t/t4018/fortran-module-procedure
+++ /dev/null
@@ -1,13 +0,0 @@
- module RIGHT
-
-   implicit none
-   private
-
-   interface letters  ! generic interface
-      module procedure aaaa, &
-                       bbbb, &
-                       ChangeMe, &
-                       dddd
-   end interface
-   
-end module RIGHT
diff --git a/t/t4018/fortran-module-procedure.ctx b/t/t4018/fortran-module-procedure.ctx
deleted file mode 100644
index 4f5ff2e4b8..0000000000
--- a/t/t4018/fortran-module-procedure.ctx
+++ /dev/null
@@ -1 +0,0 @@
-module RIGHT
diff --git a/t/t4018/fortran-module.ctx b/t/t4018/fortran-module.ctx
deleted file mode 100644
index 4f5ff2e4b8..0000000000
--- a/t/t4018/fortran-module.ctx
+++ /dev/null
@@ -1 +0,0 @@
-module RIGHT
diff --git a/t/t4018/fortran-program b/t/t4018/fortran-program
deleted file mode 100644
index 4616895e4b..0000000000
--- a/t/t4018/fortran-program
+++ /dev/null
@@ -1,5 +0,0 @@
-program RIGHT
-
-call ChangeMe
-
-end program RIGHT
diff --git a/t/t4018/fortran-program.ctx b/t/t4018/fortran-program.ctx
deleted file mode 100644
index c4e844df30..0000000000
--- a/t/t4018/fortran-program.ctx
+++ /dev/null
@@ -1 +0,0 @@
-program RIGHT
diff --git a/t/t4018/fortran.sh b/t/t4018/fortran.sh
new file mode 100755
index 0000000000..7b0c6789d3
--- /dev/null
+++ b/t/t4018/fortran.sh
@@ -0,0 +1,159 @@
+#!/bin/sh
+#
+# See ../t4018-diff-funcname.sh's test_diff_funcname()
+#
+
+test_diff_funcname 'fortran: block data' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+BLOCK DATA RIGHT
+EOF_HUNK
+       BLOCK DATA RIGHT
+       
+       COMMON /B/ C, ChangeMe
+       DATA C, ChangeMe  / 2.0, 6.0 / 
+       END 
+EOF_TEST
+
+test_diff_funcname 'fortran: comment' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+subroutine RIGHT
+EOF_HUNK
+      module a
+
+      contains
+
+      ! subroutine wrong
+      subroutine RIGHT
+      ! subroutine wrong
+
+      real ChangeMe
+
+      end subroutine RIGHT
+
+      end module a
+EOF_TEST
+
+test_diff_funcname 'fortran: comment keyword' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+subroutine RIGHT (funcA, funcB)
+EOF_HUNK
+      module a
+
+      contains
+
+      subroutine RIGHT (funcA, funcB)
+
+      real funcA  ! grid function a
+      real funcB  ! grid function b
+
+      real ChangeMe
+
+      end subroutine RIGHT
+
+      end module a
+EOF_TEST
+
+test_diff_funcname 'fortran: comment legacy' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+subroutine RIGHT
+EOF_HUNK
+      module a
+
+      contains
+
+C subroutine wrong
+      subroutine RIGHT
+C subroutine wrong
+
+      real ChangeMe
+
+      end subroutine RIGHT
+
+      end module a
+EOF_TEST
+
+test_diff_funcname 'fortran: comment legacy star' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+subroutine RIGHT
+EOF_HUNK
+      module a
+
+      contains
+
+* subroutine wrong
+      subroutine RIGHT
+* subroutine wrong
+
+      real ChangeMe
+
+      end subroutine RIGHT
+
+      end module a
+EOF_TEST
+
+test_diff_funcname 'fortran: external function' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+function RIGHT(a, b) result(c)
+EOF_HUNK
+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_TEST
+
+test_diff_funcname 'fortran: external subroutine' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+subroutine RIGHT
+EOF_HUNK
+subroutine RIGHT
+
+real ChangeMe
+
+end subroutine RIGHT
+EOF_TEST
+
+test_diff_funcname 'fortran: module' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+module RIGHT
+EOF_HUNK
+module RIGHT
+
+use ChangeMe
+
+end module RIGHT
+EOF_TEST
+
+test_diff_funcname 'fortran: module procedure' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+module RIGHT
+EOF_HUNK
+ module RIGHT
+
+   implicit none
+   private
+
+   interface letters  ! generic interface
+      module procedure aaaa, &
+                       bbbb, &
+                       ChangeMe, &
+                       dddd
+   end interface
+   
+end module RIGHT
+EOF_TEST
+
+test_diff_funcname 'fortran: program' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+program RIGHT
+EOF_HUNK
+program RIGHT
+
+call ChangeMe
+
+end program RIGHT
+EOF_TEST
diff --git a/t/t4018/fountain-scene b/t/t4018/fountain-scene
deleted file mode 100644
index 6b3257d680..0000000000
--- a/t/t4018/fountain-scene
+++ /dev/null
@@ -1,4 +0,0 @@
-EXT. STREET RIGHT OUTSIDE - DAY
-
-CHARACTER
-You didn't say the magic phrase, "ChangeMe".
diff --git a/t/t4018/fountain-scene.ctx b/t/t4018/fountain-scene.ctx
deleted file mode 100644
index bf10171418..0000000000
--- a/t/t4018/fountain-scene.ctx
+++ /dev/null
@@ -1 +0,0 @@
-EXT. STREET RIGHT OUTSIDE - DAY
diff --git a/t/t4018/fountain.sh b/t/t4018/fountain.sh
new file mode 100755
index 0000000000..02b44d6a3f
--- /dev/null
+++ b/t/t4018/fountain.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+#
+# See ../t4018-diff-funcname.sh's test_diff_funcname()
+#
+
+test_diff_funcname 'fountain: scene' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+EXT. STREET RIGHT OUTSIDE - DAY
+EOF_HUNK
+EXT. STREET RIGHT OUTSIDE - DAY
+
+CHARACTER
+You didn't say the magic phrase, "ChangeMe".
+EOF_TEST
diff --git a/t/t4018/golang-complex-function b/t/t4018/golang-complex-function
deleted file mode 100644
index e057dcefed..0000000000
--- a/t/t4018/golang-complex-function
+++ /dev/null
@@ -1,8 +0,0 @@
-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-complex-function.ctx b/t/t4018/golang-complex-function.ctx
deleted file mode 100644
index 8e8d5582ff..0000000000
--- a/t/t4018/golang-complex-function.ctx
+++ /dev/null
@@ -1 +0,0 @@
-func (t *Test) RIGHT(a Type) (Type, error) {
diff --git a/t/t4018/golang-func b/t/t4018/golang-func
deleted file mode 100644
index 8e9c9ac7c3..0000000000
--- a/t/t4018/golang-func
+++ /dev/null
@@ -1,4 +0,0 @@
-func RIGHT() {
-	a := 5
-	b := ChangeMe
-}
diff --git a/t/t4018/golang-func.ctx b/t/t4018/golang-func.ctx
deleted file mode 100644
index 88bc823813..0000000000
--- a/t/t4018/golang-func.ctx
+++ /dev/null
@@ -1 +0,0 @@
-func RIGHT() {
diff --git a/t/t4018/golang-interface b/t/t4018/golang-interface
deleted file mode 100644
index 553bedec96..0000000000
--- a/t/t4018/golang-interface
+++ /dev/null
@@ -1,4 +0,0 @@
-type RIGHT interface {
-	a() Type
-	b() ChangeMe
-}
diff --git a/t/t4018/golang-interface.ctx b/t/t4018/golang-interface.ctx
deleted file mode 100644
index 2d07f5a383..0000000000
--- a/t/t4018/golang-interface.ctx
+++ /dev/null
@@ -1 +0,0 @@
-type RIGHT interface {
diff --git a/t/t4018/golang-long-func b/t/t4018/golang-long-func
deleted file mode 100644
index ac3a77b5c4..0000000000
--- a/t/t4018/golang-long-func
+++ /dev/null
@@ -1,5 +0,0 @@
-func RIGHT(aVeryVeryVeryLongVariableName AVeryVeryVeryLongType,
-	anotherLongVariableName AnotherLongType) {
-	a := 5
-	b := ChangeMe
-}
diff --git a/t/t4018/golang-long-func.ctx b/t/t4018/golang-long-func.ctx
deleted file mode 100644
index 25635e712e..0000000000
--- a/t/t4018/golang-long-func.ctx
+++ /dev/null
@@ -1 +0,0 @@
-func RIGHT(aVeryVeryVeryLongVariableName AVeryVeryVeryLongType,
diff --git a/t/t4018/golang-struct b/t/t4018/golang-struct
deleted file mode 100644
index 5deda77fee..0000000000
--- a/t/t4018/golang-struct
+++ /dev/null
@@ -1,4 +0,0 @@
-type RIGHT struct {
-	a Type
-	b ChangeMe
-}
diff --git a/t/t4018/golang-struct.ctx b/t/t4018/golang-struct.ctx
deleted file mode 100644
index 8a1240699d..0000000000
--- a/t/t4018/golang-struct.ctx
+++ /dev/null
@@ -1 +0,0 @@
-type RIGHT struct {
diff --git a/t/t4018/golang.sh b/t/t4018/golang.sh
new file mode 100755
index 0000000000..bf22f58c12
--- /dev/null
+++ b/t/t4018/golang.sh
@@ -0,0 +1,59 @@
+#!/bin/sh
+#
+# See ../t4018-diff-funcname.sh's test_diff_funcname()
+#
+
+test_diff_funcname 'golang: complex function' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+func (t *Test) RIGHT(a Type) (Type, error) {
+EOF_HUNK
+type Test struct {
+	a Type
+}
+
+func (t *Test) RIGHT(a Type) (Type, error) {
+	t.a = a
+	return ChangeMe, nil
+}
+EOF_TEST
+
+test_diff_funcname 'golang: func' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+func RIGHT() {
+EOF_HUNK
+func RIGHT() {
+	a := 5
+	b := ChangeMe
+}
+EOF_TEST
+
+test_diff_funcname 'golang: interface' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+type RIGHT interface {
+EOF_HUNK
+type RIGHT interface {
+	a() Type
+	b() ChangeMe
+}
+EOF_TEST
+
+test_diff_funcname 'golang: long func' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+func RIGHT(aVeryVeryVeryLongVariableName AVeryVeryVeryLongType,
+EOF_HUNK
+func RIGHT(aVeryVeryVeryLongVariableName AVeryVeryVeryLongType,
+	anotherLongVariableName AnotherLongType) {
+	a := 5
+	b := ChangeMe
+}
+EOF_TEST
+
+test_diff_funcname 'golang: struct' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+type RIGHT struct {
+EOF_HUNK
+type RIGHT struct {
+	a Type
+	b ChangeMe
+}
+EOF_TEST
diff --git a/t/t4018/java-class-member-function b/t/t4018/java-class-member-function
deleted file mode 100644
index 298bc7a71b..0000000000
--- a/t/t4018/java-class-member-function
+++ /dev/null
@@ -1,8 +0,0 @@
-public class Beer
-{
-	int special;
-	public static void main(String RIGHT[])
-	{
-		System.out.print("ChangeMe");
-	}
-}
diff --git a/t/t4018/java-class-member-function.ctx b/t/t4018/java-class-member-function.ctx
deleted file mode 100644
index 2125474b68..0000000000
--- a/t/t4018/java-class-member-function.ctx
+++ /dev/null
@@ -1 +0,0 @@
-public static void main(String RIGHT[])
diff --git a/t/t4018/java.sh b/t/t4018/java.sh
new file mode 100755
index 0000000000..c89cf2f9d8
--- /dev/null
+++ b/t/t4018/java.sh
@@ -0,0 +1,18 @@
+#!/bin/sh
+#
+# See ../t4018-diff-funcname.sh's test_diff_funcname()
+#
+
+test_diff_funcname 'java: class member function' \
+	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[])
+	{
+		System.out.print("ChangeMe");
+	}
+}
+EOF_TEST
diff --git a/t/t4018/markdown-heading-indented b/t/t4018/markdown-heading-indented
deleted file mode 100644
index 1991c2bd45..0000000000
--- a/t/t4018/markdown-heading-indented
+++ /dev/null
@@ -1,6 +0,0 @@
-Indented headings are allowed, as long as the indent is no more than 3 spaces.
-
-   ### RIGHT
-
-- something
-- ChangeMe
diff --git a/t/t4018/markdown-heading-indented.ctx b/t/t4018/markdown-heading-indented.ctx
deleted file mode 100644
index 5938336743..0000000000
--- a/t/t4018/markdown-heading-indented.ctx
+++ /dev/null
@@ -1 +0,0 @@
-   ### RIGHT
diff --git a/t/t4018/markdown-heading-non-headings b/t/t4018/markdown-heading-non-headings
deleted file mode 100644
index c479c1a3f1..0000000000
--- a/t/t4018/markdown-heading-non-headings
+++ /dev/null
@@ -1,17 +0,0 @@
-Headings can be right next to other lines of the file:
-# RIGHT
-Indents of four or more spaces make a code block:
-
-    # code comment, not heading
-
-If there's no space after the final hash, it's not a heading:
-
-#hashtag
-
-Sequences of more than 6 hashes don't make a heading:
-
-####### over-enthusiastic heading
-
-So the detected heading should be right up at the start of this file.
-
-ChangeMe
diff --git a/t/t4018/markdown-heading-non-headings.ctx b/t/t4018/markdown-heading-non-headings.ctx
deleted file mode 100644
index 7e2165be6e..0000000000
--- a/t/t4018/markdown-heading-non-headings.ctx
+++ /dev/null
@@ -1 +0,0 @@
-# RIGHT
diff --git a/t/t4018/markdown.sh b/t/t4018/markdown.sh
new file mode 100755
index 0000000000..3e1c79b139
--- /dev/null
+++ b/t/t4018/markdown.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+#
+# See ../t4018-diff-funcname.sh's test_diff_funcname()
+#
+
+test_diff_funcname 'markdown: heading indented' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+   ### RIGHT
+EOF_HUNK
+Indented headings are allowed, as long as the indent is no more than 3 spaces.
+
+   ### RIGHT
+
+- something
+- ChangeMe
+EOF_TEST
+
+test_diff_funcname 'markdown: heading non headings' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+# RIGHT
+EOF_HUNK
+Headings can be right next to other lines of the file:
+# RIGHT
+Indents of four or more spaces make a code block:
+
+    # code comment, not heading
+
+If there's no space after the final hash, it's not a heading:
+
+#hashtag
+
+Sequences of more than 6 hashes don't make a heading:
+
+####### over-enthusiastic heading
+
+So the detected heading should be right up at the start of this file.
+
+ChangeMe
+EOF_TEST
diff --git a/t/t4018/matlab-class-definition b/t/t4018/matlab-class-definition
deleted file mode 100644
index 84daedfb4e..0000000000
--- a/t/t4018/matlab-class-definition
+++ /dev/null
@@ -1,5 +0,0 @@
-classdef RIGHT
-    properties
-        ChangeMe
-    end
-end
diff --git a/t/t4018/matlab-class-definition.ctx b/t/t4018/matlab-class-definition.ctx
deleted file mode 100644
index 5dd5b45628..0000000000
--- a/t/t4018/matlab-class-definition.ctx
+++ /dev/null
@@ -1 +0,0 @@
-classdef RIGHT
diff --git a/t/t4018/matlab-function b/t/t4018/matlab-function
deleted file mode 100644
index 897a9b13ff..0000000000
--- a/t/t4018/matlab-function
+++ /dev/null
@@ -1,4 +0,0 @@
-function y = RIGHT()
-x = 5;
-y = ChangeMe + x;
-end
diff --git a/t/t4018/matlab-function.ctx b/t/t4018/matlab-function.ctx
deleted file mode 100644
index 72d2350b13..0000000000
--- a/t/t4018/matlab-function.ctx
+++ /dev/null
@@ -1 +0,0 @@
-function y = RIGHT()
diff --git a/t/t4018/matlab-octave-section-1 b/t/t4018/matlab-octave-section-1
deleted file mode 100644
index 3bb6c4670e..0000000000
--- a/t/t4018/matlab-octave-section-1
+++ /dev/null
@@ -1,3 +0,0 @@
-%%% RIGHT section
-# this is octave script
-ChangeMe = 1;
diff --git a/t/t4018/matlab-octave-section-1.ctx b/t/t4018/matlab-octave-section-1.ctx
deleted file mode 100644
index ca9b349f94..0000000000
--- a/t/t4018/matlab-octave-section-1.ctx
+++ /dev/null
@@ -1 +0,0 @@
-%%% RIGHT section
diff --git a/t/t4018/matlab-octave-section-2 b/t/t4018/matlab-octave-section-2
deleted file mode 100644
index ab2980f7f2..0000000000
--- a/t/t4018/matlab-octave-section-2
+++ /dev/null
@@ -1,3 +0,0 @@
-## RIGHT section
-# this is octave script
-ChangeMe = 1;
diff --git a/t/t4018/matlab-octave-section-2.ctx b/t/t4018/matlab-octave-section-2.ctx
deleted file mode 100644
index 5cbb77faf5..0000000000
--- a/t/t4018/matlab-octave-section-2.ctx
+++ /dev/null
@@ -1 +0,0 @@
-## RIGHT section
diff --git a/t/t4018/matlab-section b/t/t4018/matlab-section
deleted file mode 100644
index 5ea59a5de0..0000000000
--- a/t/t4018/matlab-section
+++ /dev/null
@@ -1,3 +0,0 @@
-%% RIGHT section
-% this is understood by both matlab and octave
-ChangeMe = 1;
diff --git a/t/t4018/matlab-section.ctx b/t/t4018/matlab-section.ctx
deleted file mode 100644
index e83fee6f4d..0000000000
--- a/t/t4018/matlab-section.ctx
+++ /dev/null
@@ -1 +0,0 @@
-%% RIGHT section
diff --git a/t/t4018/matlab.sh b/t/t4018/matlab.sh
new file mode 100755
index 0000000000..f62289148e
--- /dev/null
+++ b/t/t4018/matlab.sh
@@ -0,0 +1,52 @@
+#!/bin/sh
+#
+# See ../t4018-diff-funcname.sh's test_diff_funcname()
+#
+
+test_diff_funcname 'matlab: class definition' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+classdef RIGHT
+EOF_HUNK
+classdef RIGHT
+    properties
+        ChangeMe
+    end
+end
+EOF_TEST
+
+test_diff_funcname 'matlab: function' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+function y = RIGHT()
+EOF_HUNK
+function y = RIGHT()
+x = 5;
+y = ChangeMe + x;
+end
+EOF_TEST
+
+test_diff_funcname 'matlab: octave section 1' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+%%% RIGHT section
+EOF_HUNK
+%%% RIGHT section
+# this is octave script
+ChangeMe = 1;
+EOF_TEST
+
+test_diff_funcname 'matlab: octave section 2' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+## RIGHT section
+EOF_HUNK
+## RIGHT section
+# this is octave script
+ChangeMe = 1;
+EOF_TEST
+
+test_diff_funcname 'matlab: section' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+%% RIGHT section
+EOF_HUNK
+%% RIGHT section
+% this is understood by both matlab and octave
+ChangeMe = 1;
+EOF_TEST
diff --git a/t/t4018/perl-skip-end-of-heredoc b/t/t4018/perl-skip-end-of-heredoc
deleted file mode 100644
index c22d39b256..0000000000
--- a/t/t4018/perl-skip-end-of-heredoc
+++ /dev/null
@@ -1,8 +0,0 @@
-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-end-of-heredoc.ctx b/t/t4018/perl-skip-end-of-heredoc.ctx
deleted file mode 100644
index c15f4b78bd..0000000000
--- a/t/t4018/perl-skip-end-of-heredoc.ctx
+++ /dev/null
@@ -1 +0,0 @@
-sub RIGHTwithheredocument {
diff --git a/t/t4018/perl-skip-forward-decl b/t/t4018/perl-skip-forward-decl
deleted file mode 100644
index a98cb8bdad..0000000000
--- a/t/t4018/perl-skip-forward-decl
+++ /dev/null
@@ -1,10 +0,0 @@
-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-forward-decl.ctx b/t/t4018/perl-skip-forward-decl.ctx
deleted file mode 100644
index e0c51599ad..0000000000
--- a/t/t4018/perl-skip-forward-decl.ctx
+++ /dev/null
@@ -1 +0,0 @@
-package RIGHT;
diff --git a/t/t4018/perl-skip-sub-in-pod b/t/t4018/perl-skip-sub-in-pod
deleted file mode 100644
index e39f02462e..0000000000
--- a/t/t4018/perl-skip-sub-in-pod
+++ /dev/null
@@ -1,18 +0,0 @@
-=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-skip-sub-in-pod.ctx b/t/t4018/perl-skip-sub-in-pod.ctx
deleted file mode 100644
index abddd76655..0000000000
--- a/t/t4018/perl-skip-sub-in-pod.ctx
+++ /dev/null
@@ -1 +0,0 @@
-=head1 SYNOPSIS_RIGHT
diff --git a/t/t4018/perl-sub-definition b/t/t4018/perl-sub-definition
deleted file mode 100644
index a507d1f645..0000000000
--- a/t/t4018/perl-sub-definition
+++ /dev/null
@@ -1,4 +0,0 @@
-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 330b3df114..0000000000
--- a/t/t4018/perl-sub-definition-kr-brace
+++ /dev/null
@@ -1,4 +0,0 @@
-sub RIGHT
-{
-	print "ChangeMe\n";
-}
diff --git a/t/t4018/perl-sub-definition-kr-brace.ctx b/t/t4018/perl-sub-definition-kr-brace.ctx
deleted file mode 100644
index 7e5aee5cde..0000000000
--- a/t/t4018/perl-sub-definition-kr-brace.ctx
+++ /dev/null
@@ -1 +0,0 @@
-sub RIGHT
diff --git a/t/t4018/perl-sub-definition.ctx b/t/t4018/perl-sub-definition.ctx
deleted file mode 100644
index d49a63598e..0000000000
--- a/t/t4018/perl-sub-definition.ctx
+++ /dev/null
@@ -1 +0,0 @@
-sub RIGHT {
diff --git a/t/t4018/perl.sh b/t/t4018/perl.sh
new file mode 100755
index 0000000000..ac8fff7417
--- /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
diff --git a/t/t4018/php-abstract-class b/t/t4018/php-abstract-class
deleted file mode 100644
index 5213e12494..0000000000
--- a/t/t4018/php-abstract-class
+++ /dev/null
@@ -1,4 +0,0 @@
-abstract class RIGHT
-{
-    const FOO = 'ChangeMe';
-}
diff --git a/t/t4018/php-abstract-class.ctx b/t/t4018/php-abstract-class.ctx
deleted file mode 100644
index f572d2129b..0000000000
--- a/t/t4018/php-abstract-class.ctx
+++ /dev/null
@@ -1 +0,0 @@
-abstract class RIGHT
diff --git a/t/t4018/php-abstract-method b/t/t4018/php-abstract-method
deleted file mode 100644
index ce215df75a..0000000000
--- a/t/t4018/php-abstract-method
+++ /dev/null
@@ -1,7 +0,0 @@
-abstract class Klass
-{
-    abstract public function RIGHT(): ?string
-    {
-        return 'ChangeMe';
-    }
-}
diff --git a/t/t4018/php-abstract-method.ctx b/t/t4018/php-abstract-method.ctx
deleted file mode 100644
index 14cb6df42e..0000000000
--- a/t/t4018/php-abstract-method.ctx
+++ /dev/null
@@ -1 +0,0 @@
-abstract public function RIGHT(): ?string
diff --git a/t/t4018/php-class b/t/t4018/php-class
deleted file mode 100644
index 7785b6303c..0000000000
--- a/t/t4018/php-class
+++ /dev/null
@@ -1,4 +0,0 @@
-class RIGHT
-{
-    const FOO = 'ChangeMe';
-}
diff --git a/t/t4018/php-class.ctx b/t/t4018/php-class.ctx
deleted file mode 100644
index 54bff816d6..0000000000
--- a/t/t4018/php-class.ctx
+++ /dev/null
@@ -1 +0,0 @@
-class RIGHT
diff --git a/t/t4018/php-final-class b/t/t4018/php-final-class
deleted file mode 100644
index 69f5710552..0000000000
--- a/t/t4018/php-final-class
+++ /dev/null
@@ -1,4 +0,0 @@
-final class RIGHT
-{
-    const FOO = 'ChangeMe';
-}
diff --git a/t/t4018/php-final-class.ctx b/t/t4018/php-final-class.ctx
deleted file mode 100644
index 4d59fb749b..0000000000
--- a/t/t4018/php-final-class.ctx
+++ /dev/null
@@ -1 +0,0 @@
-final class RIGHT
diff --git a/t/t4018/php-final-method b/t/t4018/php-final-method
deleted file mode 100644
index 537fb8ad9a..0000000000
--- a/t/t4018/php-final-method
+++ /dev/null
@@ -1,7 +0,0 @@
-class Klass
-{
-    final public function RIGHT(): string
-    {
-        return 'ChangeMe';
-    }
-}
diff --git a/t/t4018/php-final-method.ctx b/t/t4018/php-final-method.ctx
deleted file mode 100644
index b7da8f8082..0000000000
--- a/t/t4018/php-final-method.ctx
+++ /dev/null
@@ -1 +0,0 @@
-final public function RIGHT(): string
diff --git a/t/t4018/php-function b/t/t4018/php-function
deleted file mode 100644
index 35717c51c3..0000000000
--- a/t/t4018/php-function
+++ /dev/null
@@ -1,4 +0,0 @@
-function RIGHT()
-{
-    return 'ChangeMe';
-}
diff --git a/t/t4018/php-function.ctx b/t/t4018/php-function.ctx
deleted file mode 100644
index c5f3e55302..0000000000
--- a/t/t4018/php-function.ctx
+++ /dev/null
@@ -1 +0,0 @@
-function RIGHT()
diff --git a/t/t4018/php-interface b/t/t4018/php-interface
deleted file mode 100644
index 86b49ad5d9..0000000000
--- a/t/t4018/php-interface
+++ /dev/null
@@ -1,4 +0,0 @@
-interface RIGHT
-{
-    public function foo($ChangeMe);
-}
diff --git a/t/t4018/php-interface.ctx b/t/t4018/php-interface.ctx
deleted file mode 100644
index a45fa0532a..0000000000
--- a/t/t4018/php-interface.ctx
+++ /dev/null
@@ -1 +0,0 @@
-interface RIGHT
diff --git a/t/t4018/php-method b/t/t4018/php-method
deleted file mode 100644
index 03af1a6d9d..0000000000
--- a/t/t4018/php-method
+++ /dev/null
@@ -1,7 +0,0 @@
-class Klass
-{
-    public static function RIGHT()
-    {
-        return 'ChangeMe';
-    }
-}
diff --git a/t/t4018/php-method.ctx b/t/t4018/php-method.ctx
deleted file mode 100644
index eb1659ff9f..0000000000
--- a/t/t4018/php-method.ctx
+++ /dev/null
@@ -1 +0,0 @@
-public static function RIGHT()
diff --git a/t/t4018/php-trait b/t/t4018/php-trait
deleted file mode 100644
index 65b8c82a61..0000000000
--- a/t/t4018/php-trait
+++ /dev/null
@@ -1,7 +0,0 @@
-trait RIGHT
-{
-    public function foo($ChangeMe)
-    {
-        return 'foo';
-    }
-}
diff --git a/t/t4018/php-trait.ctx b/t/t4018/php-trait.ctx
deleted file mode 100644
index 57aa4c6267..0000000000
--- a/t/t4018/php-trait.ctx
+++ /dev/null
@@ -1 +0,0 @@
-trait RIGHT
diff --git a/t/t4018/php.sh b/t/t4018/php.sh
new file mode 100755
index 0000000000..e0ccb2277b
--- /dev/null
+++ b/t/t4018/php.sh
@@ -0,0 +1,106 @@
+#!/bin/sh
+#
+# See ../t4018-diff-funcname.sh's test_diff_funcname()
+#
+
+test_diff_funcname 'php: abstract class' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+abstract class RIGHT
+EOF_HUNK
+abstract class RIGHT
+{
+    const FOO = 'ChangeMe';
+}
+EOF_TEST
+
+test_diff_funcname 'php: abstract method' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+abstract public function RIGHT(): ?string
+EOF_HUNK
+abstract class Klass
+{
+    abstract public function RIGHT(): ?string
+    {
+        return 'ChangeMe';
+    }
+}
+EOF_TEST
+
+test_diff_funcname 'php: class' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+class RIGHT
+EOF_HUNK
+class RIGHT
+{
+    const FOO = 'ChangeMe';
+}
+EOF_TEST
+
+test_diff_funcname 'php: final class' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+final class RIGHT
+EOF_HUNK
+final class RIGHT
+{
+    const FOO = 'ChangeMe';
+}
+EOF_TEST
+
+test_diff_funcname 'php: final method' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+final public function RIGHT(): string
+EOF_HUNK
+class Klass
+{
+    final public function RIGHT(): string
+    {
+        return 'ChangeMe';
+    }
+}
+EOF_TEST
+
+test_diff_funcname 'php: function' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+function RIGHT()
+EOF_HUNK
+function RIGHT()
+{
+    return 'ChangeMe';
+}
+EOF_TEST
+
+test_diff_funcname 'php: interface' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+interface RIGHT
+EOF_HUNK
+interface RIGHT
+{
+    public function foo($ChangeMe);
+}
+EOF_TEST
+
+test_diff_funcname 'php: method' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+public static function RIGHT()
+EOF_HUNK
+class Klass
+{
+    public static function RIGHT()
+    {
+        return 'ChangeMe';
+    }
+}
+EOF_TEST
+
+test_diff_funcname 'php: trait' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+trait RIGHT
+EOF_HUNK
+trait RIGHT
+{
+    public function foo($ChangeMe)
+    {
+        return 'foo';
+    }
+}
+EOF_TEST
diff --git a/t/t4018/python-async-def b/t/t4018/python-async-def
deleted file mode 100644
index 87640e03d2..0000000000
--- a/t/t4018/python-async-def
+++ /dev/null
@@ -1,4 +0,0 @@
-async def RIGHT(pi: int = 3.14):
-    while True:
-        break
-    return ChangeMe()
diff --git a/t/t4018/python-async-def.ctx b/t/t4018/python-async-def.ctx
deleted file mode 100644
index 468c548bbe..0000000000
--- a/t/t4018/python-async-def.ctx
+++ /dev/null
@@ -1 +0,0 @@
-async def RIGHT(pi: int = 3.14):
diff --git a/t/t4018/python-class b/t/t4018/python-class
deleted file mode 100644
index ba9e741430..0000000000
--- a/t/t4018/python-class
+++ /dev/null
@@ -1,4 +0,0 @@
-class RIGHT(int, str):
-    # comment
-    # another comment
-    # ChangeMe
diff --git a/t/t4018/python-class.ctx b/t/t4018/python-class.ctx
deleted file mode 100644
index a40b755e29..0000000000
--- a/t/t4018/python-class.ctx
+++ /dev/null
@@ -1 +0,0 @@
-class RIGHT(int, str):
diff --git a/t/t4018/python-def b/t/t4018/python-def
deleted file mode 100644
index e50b31b0ad..0000000000
--- a/t/t4018/python-def
+++ /dev/null
@@ -1,4 +0,0 @@
-def RIGHT(pi: int = 3.14):
-    while True:
-        break
-    return ChangeMe()
diff --git a/t/t4018/python-def.ctx b/t/t4018/python-def.ctx
deleted file mode 100644
index a1a9cbad63..0000000000
--- a/t/t4018/python-def.ctx
+++ /dev/null
@@ -1 +0,0 @@
-def RIGHT(pi: int = 3.14):
diff --git a/t/t4018/python-indented-async-def b/t/t4018/python-indented-async-def
deleted file mode 100644
index f5d03258af..0000000000
--- a/t/t4018/python-indented-async-def
+++ /dev/null
@@ -1,7 +0,0 @@
-class Foo:
-    async def RIGHT(self, x: int):
-        return [
-            1,
-            2,
-            ChangeMe,
-        ]
diff --git a/t/t4018/python-indented-async-def.ctx b/t/t4018/python-indented-async-def.ctx
deleted file mode 100644
index d393620a1e..0000000000
--- a/t/t4018/python-indented-async-def.ctx
+++ /dev/null
@@ -1 +0,0 @@
-async def RIGHT(self, x: int):
diff --git a/t/t4018/python-indented-class b/t/t4018/python-indented-class
deleted file mode 100644
index 19b4f35c4c..0000000000
--- a/t/t4018/python-indented-class
+++ /dev/null
@@ -1,5 +0,0 @@
-if TYPE_CHECKING:
-    class RIGHT:
-        # comment
-        # another comment
-        # ChangeMe
diff --git a/t/t4018/python-indented-class.ctx b/t/t4018/python-indented-class.ctx
deleted file mode 100644
index 0881c84dba..0000000000
--- a/t/t4018/python-indented-class.ctx
+++ /dev/null
@@ -1 +0,0 @@
-class RIGHT:
diff --git a/t/t4018/python-indented-def b/t/t4018/python-indented-def
deleted file mode 100644
index 208fbadd2b..0000000000
--- a/t/t4018/python-indented-def
+++ /dev/null
@@ -1,7 +0,0 @@
-class Foo:
-    def RIGHT(self, x: int):
-        return [
-            1,
-            2,
-            ChangeMe,
-        ]
diff --git a/t/t4018/python-indented-def.ctx b/t/t4018/python-indented-def.ctx
deleted file mode 100644
index 6e5a44b391..0000000000
--- a/t/t4018/python-indented-def.ctx
+++ /dev/null
@@ -1 +0,0 @@
-def RIGHT(self, x: int):
diff --git a/t/t4018/python.sh b/t/t4018/python.sh
new file mode 100755
index 0000000000..ecb5736d57
--- /dev/null
+++ b/t/t4018/python.sh
@@ -0,0 +1,71 @@
+#!/bin/sh
+#
+# See ../t4018-diff-funcname.sh's test_diff_funcname()
+#
+
+test_diff_funcname 'python: async def' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+async def RIGHT(pi: int = 3.14):
+EOF_HUNK
+async def RIGHT(pi: int = 3.14):
+    while True:
+        break
+    return ChangeMe()
+EOF_TEST
+
+test_diff_funcname 'python: class' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+class RIGHT(int, str):
+EOF_HUNK
+class RIGHT(int, str):
+    # comment
+    # another comment
+    # ChangeMe
+EOF_TEST
+
+test_diff_funcname 'python: def' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+def RIGHT(pi: int = 3.14):
+EOF_HUNK
+def RIGHT(pi: int = 3.14):
+    while True:
+        break
+    return ChangeMe()
+EOF_TEST
+
+test_diff_funcname 'python: indented async def' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+async def RIGHT(self, x: int):
+EOF_HUNK
+class Foo:
+    async def RIGHT(self, x: int):
+        return [
+            1,
+            2,
+            ChangeMe,
+        ]
+EOF_TEST
+
+test_diff_funcname 'python: indented class' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+class RIGHT:
+EOF_HUNK
+if TYPE_CHECKING:
+    class RIGHT:
+        # comment
+        # another comment
+        # ChangeMe
+EOF_TEST
+
+test_diff_funcname 'python: indented def' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+def RIGHT(self, x: int):
+EOF_HUNK
+class Foo:
+    def RIGHT(self, x: int):
+        return [
+            1,
+            2,
+            ChangeMe,
+        ]
+EOF_TEST
diff --git a/t/t4018/rust-fn b/t/t4018/rust-fn
deleted file mode 100644
index cbe02155f1..0000000000
--- a/t/t4018/rust-fn
+++ /dev/null
@@ -1,5 +0,0 @@
-pub(self) fn RIGHT<T>(x: &[T]) where T: Debug {
-    let _ = x;
-    // a comment
-    let a = ChangeMe;
-}
diff --git a/t/t4018/rust-fn.ctx b/t/t4018/rust-fn.ctx
deleted file mode 100644
index baa37cf253..0000000000
--- a/t/t4018/rust-fn.ctx
+++ /dev/null
@@ -1 +0,0 @@
-pub(self) fn RIGHT<T>(x: &[T]) where T: Debug {
diff --git a/t/t4018/rust-impl b/t/t4018/rust-impl
deleted file mode 100644
index 09df3cd93b..0000000000
--- a/t/t4018/rust-impl
+++ /dev/null
@@ -1,5 +0,0 @@
-impl<'a, T: AsRef<[u8]>>  std::RIGHT for Git<'a> {
-
-    pub fn ChangeMe(&self) -> () {
-    }
-}
diff --git a/t/t4018/rust-impl.ctx b/t/t4018/rust-impl.ctx
deleted file mode 100644
index 5344c35f3f..0000000000
--- a/t/t4018/rust-impl.ctx
+++ /dev/null
@@ -1 +0,0 @@
-impl<'a, T: AsRef<[u8]>>  std::RIGHT for Git<'a> {
diff --git a/t/t4018/rust-macro-rules b/t/t4018/rust-macro-rules
deleted file mode 100644
index ec610c5b62..0000000000
--- a/t/t4018/rust-macro-rules
+++ /dev/null
@@ -1,6 +0,0 @@
-macro_rules! RIGHT {
-    () => {
-        // a comment
-        let x = ChangeMe;
-    };
-}
diff --git a/t/t4018/rust-macro-rules.ctx b/t/t4018/rust-macro-rules.ctx
deleted file mode 100644
index 7520463aa0..0000000000
--- a/t/t4018/rust-macro-rules.ctx
+++ /dev/null
@@ -1 +0,0 @@
-macro_rules! RIGHT {
diff --git a/t/t4018/rust-struct b/t/t4018/rust-struct
deleted file mode 100644
index 76aff1c0d8..0000000000
--- a/t/t4018/rust-struct
+++ /dev/null
@@ -1,5 +0,0 @@
-#[derive(Debug)]
-pub(super) struct RIGHT<'a> {
-    name: &'a str,
-    age: ChangeMe,
-}
diff --git a/t/t4018/rust-struct.ctx b/t/t4018/rust-struct.ctx
deleted file mode 100644
index c1e09dc808..0000000000
--- a/t/t4018/rust-struct.ctx
+++ /dev/null
@@ -1 +0,0 @@
-pub(super) struct RIGHT<'a> {
diff --git a/t/t4018/rust-trait b/t/t4018/rust-trait
deleted file mode 100644
index ea397f09ed..0000000000
--- a/t/t4018/rust-trait
+++ /dev/null
@@ -1,5 +0,0 @@
-unsafe trait RIGHT<T> {
-    fn len(&self) -> u32;
-    fn ChangeMe(&self, n: u32) -> T;
-    fn iter<F>(&self, f: F) where F: Fn(T);
-}
diff --git a/t/t4018/rust-trait.ctx b/t/t4018/rust-trait.ctx
deleted file mode 100644
index 6af803db29..0000000000
--- a/t/t4018/rust-trait.ctx
+++ /dev/null
@@ -1 +0,0 @@
-unsafe trait RIGHT<T> {
diff --git a/t/t4018/rust.sh b/t/t4018/rust.sh
new file mode 100755
index 0000000000..ba018c6b95
--- /dev/null
+++ b/t/t4018/rust.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+#
+# See ../t4018-diff-funcname.sh's test_diff_funcname()
+#
+
+test_diff_funcname 'rust: fn' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+pub(self) fn RIGHT<T>(x: &[T]) where T: Debug {
+EOF_HUNK
+pub(self) fn RIGHT<T>(x: &[T]) where T: Debug {
+    let _ = x;
+    // a comment
+    let a = ChangeMe;
+}
+EOF_TEST
+
+test_diff_funcname 'rust: impl' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+impl<'a, T: AsRef<[u8]>>  std::RIGHT for Git<'a> {
+EOF_HUNK
+impl<'a, T: AsRef<[u8]>>  std::RIGHT for Git<'a> {
+
+    pub fn ChangeMe(&self) -> () {
+    }
+}
+EOF_TEST
+
+test_diff_funcname 'rust: macro rules' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+macro_rules! RIGHT {
+EOF_HUNK
+macro_rules! RIGHT {
+    () => {
+        // a comment
+        let x = ChangeMe;
+    };
+}
+EOF_TEST
+
+test_diff_funcname 'rust: struct' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+pub(super) struct RIGHT<'a> {
+EOF_HUNK
+#[derive(Debug)]
+pub(super) struct RIGHT<'a> {
+    name: &'a str,
+    age: ChangeMe,
+}
+EOF_TEST
+
+test_diff_funcname 'rust: trait' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+unsafe trait RIGHT<T> {
+EOF_HUNK
+unsafe trait RIGHT<T> {
+    fn len(&self) -> u32;
+    fn ChangeMe(&self, n: u32) -> T;
+    fn iter<F>(&self, f: F) where F: Fn(T);
+}
+EOF_TEST
-- 
2.30.0.284.gd98b1dd5eaa7


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

* [PATCH v2 13/27] userdiff tests: do config teardown in test_diff_funcname()
  2021-02-15  0:52       ` [PATCH 00/20] userdiff: refactor + test + doc + misc improvements Ævar Arnfjörð Bjarmason
                           ` (12 preceding siblings ...)
  2021-02-15 15:44         ` [PATCH v2 12/27] userdiff tests: rewrite hunk header test infrastructure Ævar Arnfjörð Bjarmason
@ 2021-02-15 15:44         ` Ævar Arnfjörð Bjarmason
  2021-02-15 15:44         ` [PATCH v2 14/27] userdiff tests: move custom patterns into one test file Ævar Arnfjörð Bjarmason
                           ` (13 subsequent siblings)
  27 siblings, 0 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15 15:44 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers, Eric Sunshine, Chris Torek,
	Ævar Arnfjörð Bjarmason

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 <avarab@gmail.com>
---
 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 decf7961f9..4cb0b7ba2b 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -78,6 +78,13 @@ test_diff_funcname () {
 		git diff -U1 "$what" >diff &&
 		sed -n -e "s/^.*@@$//p" -e "s/^.*@@ //p" <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


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

* [PATCH v2 14/27] userdiff tests: move custom patterns into one test file
  2021-02-15  0:52       ` [PATCH 00/20] userdiff: refactor + test + doc + misc improvements Ævar Arnfjörð Bjarmason
                           ` (13 preceding siblings ...)
  2021-02-15 15:44         ` [PATCH v2 13/27] userdiff tests: do config teardown in test_diff_funcname() Ævar Arnfjörð Bjarmason
@ 2021-02-15 15:44         ` Ævar Arnfjörð Bjarmason
  2021-02-15 15:44         ` [PATCH v2 15/27] userdiff tests: remove hack for "RIGHT" token Ævar Arnfjörð Bjarmason
                           ` (12 subsequent siblings)
  27 siblings, 0 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15 15:44 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers, Eric Sunshine, Chris Torek,
	Ævar Arnfjörð Bjarmason

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 <avarab@gmail.com>
---
 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 4cb0b7ba2b..d80a2ad4a4 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -11,18 +11,6 @@ test_expect_success 'setup' '
 	builtin_drivers=$(test-tool userdiff list-builtin-drivers) &&
 	test -n "$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
@@ -30,9 +18,7 @@ test_expect_success 'setup' '
 
 diffpatterns="
 	$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 0000000000..59d855c01c
--- /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 f8bbccadb4..0000000000
--- 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 c68421f788..0000000000
--- 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 07c5c134ff..0000000000
--- 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


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

* [PATCH v2 15/27] userdiff tests: remove hack for "RIGHT" token
  2021-02-15  0:52       ` [PATCH 00/20] userdiff: refactor + test + doc + misc improvements Ævar Arnfjörð Bjarmason
                           ` (14 preceding siblings ...)
  2021-02-15 15:44         ` [PATCH v2 14/27] userdiff tests: move custom patterns into one test file Ævar Arnfjörð Bjarmason
@ 2021-02-15 15:44         ` Ævar Arnfjörð Bjarmason
  2021-02-15 15:44         ` [PATCH v2 16/27] userdiff tests: do not do compile tests on "custom" pattern Ævar Arnfjörð Bjarmason
                           ` (11 subsequent siblings)
  27 siblings, 0 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15 15:44 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers, Eric Sunshine, Chris Torek,
	Ævar Arnfjörð Bjarmason

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 <avarab@gmail.com>
---
 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 59d855c01c..b208a771d2 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


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

* [PATCH v2 16/27] userdiff tests: do not do compile tests on "custom" pattern
  2021-02-15  0:52       ` [PATCH 00/20] userdiff: refactor + test + doc + misc improvements Ævar Arnfjörð Bjarmason
                           ` (15 preceding siblings ...)
  2021-02-15 15:44         ` [PATCH v2 15/27] userdiff tests: remove hack for "RIGHT" token Ævar Arnfjörð Bjarmason
@ 2021-02-15 15:44         ` Ævar Arnfjörð Bjarmason
  2021-02-15 15:44         ` [PATCH v2 17/27] userdiff tests + docs: document & test "diff.<driver>.x?funcname" Ævar Arnfjörð Bjarmason
                           ` (10 subsequent siblings)
  27 siblings, 0 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15 15:44 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers, Eric Sunshine, Chris Torek,
	Ævar Arnfjörð Bjarmason

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 <avarab@gmail.com>
---
 t/t4018-diff-funcname.sh | 9 ++-------
 1 file changed, 2 insertions(+), 7 deletions(-)

diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index d80a2ad4a4..3ba9d657b1 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -16,12 +16,7 @@ test_expect_success 'setup' '
 	echo B >B.java
 '
 
-diffpatterns="
-	$builtin_drivers
-	custom
-"
-
-for p in $diffpatterns
+for p in $builtin_drivers
 do
 	test_expect_success "builtin $p pattern compiles" '
 		echo "*.java diff=$p" >.gitattributes &&
@@ -74,7 +69,7 @@ test_diff_funcname () {
 	'
 }
 
-for what in $diffpatterns
+for what in $builtin_drivers custom
 do
 	test="$TEST_DIRECTORY/t4018/$what.sh"
 	if ! test -e "$test"
-- 
2.30.0.284.gd98b1dd5eaa7


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

* [PATCH v2 17/27] userdiff tests + docs: document & test "diff.<driver>.x?funcname"
  2021-02-15  0:52       ` [PATCH 00/20] userdiff: refactor + test + doc + misc improvements Ævar Arnfjörð Bjarmason
                           ` (16 preceding siblings ...)
  2021-02-15 15:44         ` [PATCH v2 16/27] userdiff tests: do not do compile tests on "custom" pattern Ævar Arnfjörð Bjarmason
@ 2021-02-15 15:44         ` Ævar Arnfjörð Bjarmason
  2021-02-15 15:44         ` [PATCH v2 18/27] gitattributes doc: reword discussion of built-in userdiff patterns Ævar Arnfjörð Bjarmason
                           ` (9 subsequent siblings)
  27 siblings, 0 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15 15:44 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers, Eric Sunshine, Chris Torek,
	Ævar Arnfjörð Bjarmason

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 <avarab@gmail.com>
---
 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 2d3331f55c..6f39ef1da9 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 b208a771d2..72d38dad68 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


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

* [PATCH v2 18/27] gitattributes doc: reword discussion of built-in userdiff patterns
  2021-02-15  0:52       ` [PATCH 00/20] userdiff: refactor + test + doc + misc improvements Ævar Arnfjörð Bjarmason
                           ` (17 preceding siblings ...)
  2021-02-15 15:44         ` [PATCH v2 17/27] userdiff tests + docs: document & test "diff.<driver>.x?funcname" Ævar Arnfjörð Bjarmason
@ 2021-02-15 15:44         ` Ævar Arnfjörð Bjarmason
  2021-02-16 23:57           ` Junio C Hamano
  2021-02-15 15:44         ` [PATCH v2 19/27] gitattributes doc: document multi-line " Ævar Arnfjörð Bjarmason
                           ` (8 subsequent siblings)
  27 siblings, 1 reply; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15 15:44 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers, Eric Sunshine, Chris Torek,
	Ævar Arnfjörð Bjarmason

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 <sunshine@sunshineco.com>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 Documentation/gitattributes.txt | 16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)

diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index e84e104f93..62c1147ba9 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


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

* [PATCH v2 19/27] gitattributes doc: document multi-line userdiff patterns
  2021-02-15  0:52       ` [PATCH 00/20] userdiff: refactor + test + doc + misc improvements Ævar Arnfjörð Bjarmason
                           ` (18 preceding siblings ...)
  2021-02-15 15:44         ` [PATCH v2 18/27] gitattributes doc: reword discussion of built-in userdiff patterns Ævar Arnfjörð Bjarmason
@ 2021-02-15 15:44         ` Ævar Arnfjörð Bjarmason
  2021-02-15 18:18           ` Johannes Sixt
  2021-02-17  0:03           ` Junio C Hamano
  2021-02-15 15:44         ` [PATCH v2 20/27] userdiff tests: remove "funcname" from custom3 test Ævar Arnfjörð Bjarmason
                           ` (7 subsequent siblings)
  27 siblings, 2 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15 15:44 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers, Eric Sunshine, Chris Torek,
	Ævar Arnfjörð Bjarmason

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 <avarab@gmail.com>
---
 Documentation/gitattributes.txt | 33 +++++++++++++++++++---
 t/t4018/custom.sh               | 50 +++++++++++++++++++++++++++++++++
 t/t4018/perl.sh                 | 16 +++++++++++
 3 files changed, 95 insertions(+), 4 deletions(-)

diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index 62c1147ba9..b51d2c86e0 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -794,12 +794,37 @@ 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 at a time from left to
+right. Do not supply a trailing "\n" for the last pattern. 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
+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 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 72d38dad68..30df13d8b2 100755
--- a/t/t4018/custom.sh
+++ b/t/t4018/custom.sh
@@ -111,3 +111,53 @@ 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
diff --git a/t/t4018/perl.sh b/t/t4018/perl.sh
index ac8fff7417..2952483a2c 100755
--- a/t/t4018/perl.sh
+++ b/t/t4018/perl.sh
@@ -76,3 +76,19 @@ sub RIGHT
 	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


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

* [PATCH v2 20/27] userdiff tests: remove "funcname" from custom3 test
  2021-02-15  0:52       ` [PATCH 00/20] userdiff: refactor + test + doc + misc improvements Ævar Arnfjörð Bjarmason
                           ` (19 preceding siblings ...)
  2021-02-15 15:44         ` [PATCH v2 19/27] gitattributes doc: document multi-line " Ævar Arnfjörð Bjarmason
@ 2021-02-15 15:44         ` Ævar Arnfjörð Bjarmason
  2021-02-15 15:44         ` [PATCH v2 21/27] userdiff tests: factor out test_diff_funcname() logic Ævar Arnfjörð Bjarmason
                           ` (6 subsequent siblings)
  27 siblings, 0 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15 15:44 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers, Eric Sunshine, Chris Torek,
	Ævar Arnfjörð Bjarmason

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 <avarab@gmail.com>
---
 t/t4018/custom.sh | 1 -
 1 file changed, 1 deletion(-)

diff --git a/t/t4018/custom.sh b/t/t4018/custom.sh
index 30df13d8b2..886de9cddb 100755
--- a/t/t4018/custom.sh
+++ b/t/t4018/custom.sh
@@ -51,7 +51,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


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

* [PATCH v2 21/27] userdiff tests: factor out test_diff_funcname() logic
  2021-02-15  0:52       ` [PATCH 00/20] userdiff: refactor + test + doc + misc improvements Ævar Arnfjörð Bjarmason
                           ` (20 preceding siblings ...)
  2021-02-15 15:44         ` [PATCH v2 20/27] userdiff tests: remove "funcname" from custom3 test Ævar Arnfjörð Bjarmason
@ 2021-02-15 15:44         ` Ævar Arnfjörð Bjarmason
  2021-02-15 15:44         ` [PATCH v2 22/27] userdiff tests: test hunk headers on accumulated files Ævar Arnfjörð Bjarmason
                           ` (5 subsequent siblings)
  27 siblings, 0 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15 15:44 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers, Eric Sunshine, Chris Torek,
	Ævar Arnfjörð Bjarmason

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 <avarab@gmail.com>
---
 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 3ba9d657b1..2efe4e5bdd 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -41,6 +41,17 @@ test_expect_success 'last regexp must not be negated' '
 	test_i18ngrep ": Last expression must not be negated:" msg
 '
 
+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
+}
+
 test_diff_funcname () {
 	desc=$1
 	cat <&8 >arg.header &&
@@ -51,13 +62,12 @@ 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"
 	' &&
 
 	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
 	' &&
 
-- 
2.30.0.284.gd98b1dd5eaa7


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

* [PATCH v2 22/27] userdiff tests: test hunk headers on accumulated files
  2021-02-15  0:52       ` [PATCH 00/20] userdiff: refactor + test + doc + misc improvements Ævar Arnfjörð Bjarmason
                           ` (21 preceding siblings ...)
  2021-02-15 15:44         ` [PATCH v2 21/27] userdiff tests: factor out test_diff_funcname() logic Ævar Arnfjörð Bjarmason
@ 2021-02-15 15:44         ` Ævar Arnfjörð Bjarmason
  2021-02-15 18:29           ` Johannes Sixt
  2021-02-15 15:44         ` [PATCH v2 23/27] userdiff tests: test hunk header selection with -U0 Ævar Arnfjörð Bjarmason
                           ` (4 subsequent siblings)
  27 siblings, 1 reply; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15 15:44 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers, Eric Sunshine, Chris Torek,
	Ævar Arnfjörð Bjarmason

The existing tests in "t/t4018/" are unrealistic in that they're all
setting up small few-line isolated test cases with one thing we could
match as a hunk header, right above the one change in the file.

Expand those tests by accumulating changes within the same file type
in the "test_diff_funcname" function. So e.g. for "bash" we'll end up
a "bash.acc" file with 15 s/ChangeMe/IWasChanged/ changes.

This stress tests whether the hunk header selection will "jump across"
to an earlier change because the match for that is greedier.

As it turns out we had one false positive in "t/t4018/cpp.sh" and
"t4018/matlab.sh" because of how the tests were structured, we must
always give the "ChangeMe" line at least one line of separation from
the header, since it was at the end of those tests we'd select the
"wrong" header. Let's adjust the spacing to compensate.

So in the end we found nothing of interest here, regardless, I think
it is useful to continue to test in this mode. It's likely to aid in
finding bugs in combinations of our positive and negative matching as
we add more built-in patterns.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 t/t4018-diff-funcname.sh | 19 +++++++++++++++++++
 t/t4018/cpp.sh           |  1 +
 t/t4018/matlab.sh        |  3 +++
 3 files changed, 23 insertions(+)

diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index 2efe4e5bdd..8b4500037f 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -65,12 +65,26 @@ test_diff_funcname () {
 		do_change_me "$what"
 	' &&
 
+	test_expect_success "setup: $desc (accumulated)" '
+		cat arg.test >>arg.tests &&
+		cp arg.tests "$what".acc &&
+		git add "$what".acc &&
+		do_change_me "$what".acc
+	' &&
+
 	test_expect_success "$desc" '
 		git diff -U1 "$what" >diff &&
 		last_diff_context_line diff >actual &&
 		test_cmp expected actual
 	' &&
 
+	test_expect_success "$desc (accumulated)" '
+		git diff -U1 "$what".acc >diff &&
+		last_diff_context_line diff >actual.lines &&
+		tail -n 1 actual.lines >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
@@ -93,6 +107,11 @@ do
 		echo "$what" >arg.what
 	' &&
 
+	test_expect_success "setup: hunk header for $what (accumulated)" '
+		>arg.tests &&
+		echo "$what.acc diff=$what" >>.gitattributes
+	' &&
+
 	. "$test"
 done
 
diff --git a/t/t4018/cpp.sh b/t/t4018/cpp.sh
index 185d40d5ef..e0ab749316 100755
--- a/t/t4018/cpp.sh
+++ b/t/t4018/cpp.sh
@@ -206,6 +206,7 @@ void wrong()
 struct RIGHT_iterator_tag {};
 
 int ChangeMe;
+
 EOF_TEST
 
 test_diff_funcname 'cpp: template function definition' \
diff --git a/t/t4018/matlab.sh b/t/t4018/matlab.sh
index f62289148e..fba410e6f5 100755
--- a/t/t4018/matlab.sh
+++ b/t/t4018/matlab.sh
@@ -31,6 +31,7 @@ EOF_HUNK
 %%% RIGHT section
 # this is octave script
 ChangeMe = 1;
+
 EOF_TEST
 
 test_diff_funcname 'matlab: octave section 2' \
@@ -40,6 +41,7 @@ EOF_HUNK
 ## RIGHT section
 # this is octave script
 ChangeMe = 1;
+
 EOF_TEST
 
 test_diff_funcname 'matlab: section' \
@@ -49,4 +51,5 @@ EOF_HUNK
 %% RIGHT section
 % this is understood by both matlab and octave
 ChangeMe = 1;
+
 EOF_TEST
-- 
2.30.0.284.gd98b1dd5eaa7


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

* [PATCH v2 23/27] userdiff tests: test hunk header selection with -U0
  2021-02-15  0:52       ` [PATCH 00/20] userdiff: refactor + test + doc + misc improvements Ævar Arnfjörð Bjarmason
                           ` (22 preceding siblings ...)
  2021-02-15 15:44         ` [PATCH v2 22/27] userdiff tests: test hunk headers on accumulated files Ævar Arnfjörð Bjarmason
@ 2021-02-15 15:44         ` Ævar Arnfjörð Bjarmason
  2021-02-15 19:09           ` Johannes Sixt
  2021-02-15 15:44         ` [PATCH v2 24/27] userdiff tests: assert empty hunk header context on -U<large> Ævar Arnfjörð Bjarmason
                           ` (3 subsequent siblings)
  27 siblings, 1 reply; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15 15:44 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers, Eric Sunshine, Chris Torek,
	Ævar Arnfjörð Bjarmason

The userdiff tests have used a custom -U1 context since
f12c66b9bb (userdiff/perl: anchor "sub" and "package" patterns on the
left, 2011-05-21). Changing it to -U0 doesn't change the results for
any of the tests, except one.

Let's test for this case explicitly. I.e. that we go "beyond" the
selected context to find our hunk header. In many cases the desired
hunk header is part of the diff itself under -U1.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 t/t4018-diff-funcname.sh | 13 +++++++++++++
 t/t4018/custom.sh        |  1 +
 2 files changed, 14 insertions(+)

diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index 8b4500037f..d41aed9ba2 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -57,6 +57,7 @@ test_diff_funcname () {
 	cat <&8 >arg.header &&
 	cat <&9 >arg.test &&
 	what=$(cat arg.what) &&
+	arg_diff_U0=$2 &&
 
 	test_expect_success "setup: $desc" '
 		cp arg.test "$what" &&
@@ -78,6 +79,18 @@ test_diff_funcname () {
 		test_cmp expected actual
 	' &&
 
+	test_expect_success "$desc -U0" '
+		git diff -U0 "$what" >diff &&
+		last_diff_context_line diff >actual &&
+		if test -n "$arg_diff_U0"
+		then
+			echo "$arg_diff_U0" >new-expected &&
+			test_cmp new-expected actual
+		else
+			test_cmp expected actual
+		fi
+	' &&
+
 	test_expect_success "$desc (accumulated)" '
 		git diff -U1 "$what".acc >diff &&
 		last_diff_context_line diff >actual.lines &&
diff --git a/t/t4018/custom.sh b/t/t4018/custom.sh
index 886de9cddb..a090f7bfc2 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' \
+	'System.out.print(x + " bottles of beer on the wall "' \
 	8<<\EOF_HUNK 9<<\EOF_TEST
 int special, RIGHT;
 EOF_HUNK
-- 
2.30.0.284.gd98b1dd5eaa7


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

* [PATCH v2 24/27] userdiff tests: assert empty hunk header context on -U<large>
  2021-02-15  0:52       ` [PATCH 00/20] userdiff: refactor + test + doc + misc improvements Ævar Arnfjörð Bjarmason
                           ` (23 preceding siblings ...)
  2021-02-15 15:44         ` [PATCH v2 23/27] userdiff tests: test hunk header selection with -U0 Ævar Arnfjörð Bjarmason
@ 2021-02-15 15:44         ` Ævar Arnfjörð Bjarmason
  2021-02-15 15:44         ` [PATCH v2 25/27] userdiff: match "package" in diff=golang Ævar Arnfjörð Bjarmason
                           ` (2 subsequent siblings)
  27 siblings, 0 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15 15:44 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers, Eric Sunshine, Chris Torek,
	Ævar Arnfjörð Bjarmason

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 <avarab@gmail.com>
---
 t/t4018-diff-funcname.sh | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index d41aed9ba2..80f35c5e16 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -98,6 +98,14 @@ 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 &&
+		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


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

* [PATCH v2 25/27] userdiff: match "package" in diff=golang
  2021-02-15  0:52       ` [PATCH 00/20] userdiff: refactor + test + doc + misc improvements Ævar Arnfjörð Bjarmason
                           ` (24 preceding siblings ...)
  2021-02-15 15:44         ` [PATCH v2 24/27] userdiff tests: assert empty hunk header context on -U<large> Ævar Arnfjörð Bjarmason
@ 2021-02-15 15:44         ` Ævar Arnfjörð Bjarmason
  2021-02-15 15:44         ` [PATCH v2 26/27] userdiff tests: add basic test for ada Ævar Arnfjörð Bjarmason
  2021-02-15 15:44         ` [PATCH v2 27/27] userdiff tests: add basic test for ruby Ævar Arnfjörð Bjarmason
  27 siblings, 0 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15 15:44 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers, Eric Sunshine, Chris Torek,
	Ævar Arnfjörð Bjarmason

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.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 t/t4018/golang.sh | 10 ++++++++++
 userdiff.c        |  2 ++
 2 files changed, 12 insertions(+)

diff --git a/t/t4018/golang.sh b/t/t4018/golang.sh
index bf22f58c12..cdf9d6f8aa 100755
--- a/t/t4018/golang.sh
+++ b/t/t4018/golang.sh
@@ -3,6 +3,16 @@
 # See ../t4018-diff-funcname.sh's test_diff_funcname()
 #
 
+test_diff_funcname 'golang: package' \
+	8<<\EOF_HUNK 9<<\EOF_TEST
+package main
+EOF_HUNK
+package main
+
+import "fmt"
+// ChangeMe
+EOF_TEST
+
 test_diff_funcname 'golang: complex function' \
 	8<<\EOF_HUNK 9<<\EOF_TEST
 func (t *Test) RIGHT(a Type) (Type, error) {
diff --git a/userdiff.c b/userdiff.c
index 55f4f769bd..f975aac8fe 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -128,6 +128,8 @@ IPATTERN("fountain",
 	 /* -- */
 	 "[^ \t-]+"),
 PATTERNS("golang",
+	 /* Packages */
+	 "^[ \t]*(package[ \t]*(.*))\n"
 	 /* Functions */
 	 "^[ \t]*(func[ \t]*.*(\\{[ \t]*)?)\n"
 	 /* Structs and interfaces */
-- 
2.30.0.284.gd98b1dd5eaa7


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

* [PATCH v2 26/27] userdiff tests: add basic test for ada
  2021-02-15  0:52       ` [PATCH 00/20] userdiff: refactor + test + doc + misc improvements Ævar Arnfjörð Bjarmason
                           ` (25 preceding siblings ...)
  2021-02-15 15:44         ` [PATCH v2 25/27] userdiff: match "package" in diff=golang Ævar Arnfjörð Bjarmason
@ 2021-02-15 15:44         ` Ævar Arnfjörð Bjarmason
  2021-02-15 15:44         ` [PATCH v2 27/27] userdiff tests: add basic test for ruby Ævar Arnfjörð Bjarmason
  27 siblings, 0 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15 15:44 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers, Eric Sunshine, Chris Torek,
	Æ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
---
 t/t4018/ada.sh | 37 +++++++++++++++++++++++++++++++++++++
 1 file changed, 37 insertions(+)
 create mode 100755 t/t4018/ada.sh

diff --git a/t/t4018/ada.sh b/t/t4018/ada.sh
new file mode 100755
index 0000000000..45fc2c7a3b
--- /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


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

* [PATCH v2 27/27] userdiff tests: add basic test for ruby
  2021-02-15  0:52       ` [PATCH 00/20] userdiff: refactor + test + doc + misc improvements Ævar Arnfjörð Bjarmason
                           ` (26 preceding siblings ...)
  2021-02-15 15:44         ` [PATCH v2 26/27] userdiff tests: add basic test for ada Ævar Arnfjörð Bjarmason
@ 2021-02-15 15:44         ` Ævar Arnfjörð Bjarmason
  27 siblings, 0 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15 15:44 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, Jeff King, Jonathan Nieder,
	Philippe Blain, Adam Spiers, Eric Sunshine, Chris Torek,
	Ævar Arnfjörð Bjarmason

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 <avarab@gmail.com>
---
 t/t4018/ruby.sh | 58 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 58 insertions(+)
 create mode 100755 t/t4018/ruby.sh

diff --git a/t/t4018/ruby.sh b/t/t4018/ruby.sh
new file mode 100755
index 0000000000..1e9bfef863
--- /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' \
+	"class Two" \
+	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


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

* [PATCH 0/2] diff: do not display hunk context under -W
  2021-02-15 15:44         ` [PATCH v2 00/27] " Ævar Arnfjörð Bjarmason
@ 2021-02-15 15:50           ` Ævar Arnfjörð Bjarmason
  2021-02-15 15:50           ` [PATCH 1/2] " Ævar Arnfjörð Bjarmason
                             ` (39 subsequent siblings)
  40 siblings, 0 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15 15:50 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, René Scharfe, Vegard Nossum, Jeff King,
	Ævar Arnfjörð Bjarmason

This goes on top of my
https://lore.kernel.org/git/20210215154427.32693-1-avarab@gmail.com/
because it use its newly setup test infrastructure.

Ævar Arnfjörð Bjarmason (2):
  diff: do not display hunk context under -W
  diff: test and document -W interaction with -U<n>

 Documentation/diff-options.txt | 12 ++++++++++++
 t/t4015-diff-whitespace.sh     |  2 +-
 t/t4018-diff-funcname.sh       | 12 ++++++++++++
 xdiff/xemit.c                  |  4 +++-
 4 files changed, 28 insertions(+), 2 deletions(-)

-- 
2.30.0.284.gd98b1dd5eaa7


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

* [PATCH 1/2] diff: do not display hunk context under -W
  2021-02-15 15:44         ` [PATCH v2 00/27] " Ævar Arnfjörð Bjarmason
  2021-02-15 15:50           ` [PATCH 0/2] diff: do not display hunk context under -W Ævar Arnfjörð Bjarmason
@ 2021-02-15 15:50           ` Ævar Arnfjörð Bjarmason
  2021-02-15 18:47             ` René Scharfe.
  2021-02-16  1:30             ` Junio C Hamano
  2021-02-15 15:50           ` [PATCH 2/2] diff: test and document -W interaction with -U<n> Ævar Arnfjörð Bjarmason
                             ` (38 subsequent siblings)
  40 siblings, 2 replies; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15 15:50 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, René Scharfe, Vegard Nossum, Jeff King,
	Ævar Arnfjörð Bjarmason

Fix what I believe to be a long-standing bug in how "-W" interacts
with displaying the hunk context on the @@ line: It should not be
displayed at all under -W.

The long-standing semantics of how -W works and interacts with -U<n>
are rather easy to reason about:

 * -W extends the context line up to the start of the function. With
    userdiff this means the language-aware regex rules in userdiff.c,
    or user-supplied rules.

 * -U<n>, which defaults to -U3 shows at least <n> lines of context,
    if that's greater than what we'd extend the context to under -W
    then -U<n> wins.

 * When showing the hunk context we look up from the first line we
   show of the diff, and find whatever looks like useful context above
   that line.

Thus in e.g. the xdiff/xemit.c change being made in this commit we'll
correctly show "xdl_emit_diff()" in the hunk context under default
diff settings.

But if we viewed it with the -W option we'd show "is_empty_rec()",
because we'd first find the "xdl_emit_diff()" context line, extend the
diff to that, and then would go look for context to show again.

I don't think this behavior makes any sense, our context in this case
is what we're guaranteed to show as part of the diff itself.

The user already asked us to find that context line and show it, we
don't need to then start showing the context above that line, which
they didn't ask for.

This new behavior does give us the edge case that if we e.g. view the
diff here with "-U150 -W" we'd previously extend the context to the
middle of the "is_func_rec()" function, and show that function in the
hunk context. Now we'll show nothing.

I think that change also makes sense. We're showing a change in the
"xdl_emit_diff()" function. That's our context for the change. It
doesn't make sense with -W to start fishing around for other
context.

Arguably in that case we could save away the context we found in the
"XDL_EMIT_FUNCCONTEXT" in "xdl_emit_diff()" and show that if we end up
extending the diff past the function, either because of a high -U<n>
value, or because our change was right at the start.

I wouldn't really mind if we did that, perhaps it would be a useful
marker with high -U<n> values to remind the user of what they're
looking at, but I also don't see the usefulness in practice, so let's
punt that for now.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 Documentation/diff-options.txt | 4 ++++
 t/t4015-diff-whitespace.sh     | 2 +-
 t/t4018-diff-funcname.sh       | 7 +++++++
 xdiff/xemit.c                  | 4 +++-
 4 files changed, 15 insertions(+), 2 deletions(-)

diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index e5733ccb2d..8ca59effa7 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -759,6 +759,10 @@ endif::git-format-patch[]
 	The function names are determined in the same way as
 	`git diff` works out patch hunk headers (see 'Defining a
 	custom hunk-header' in linkgit:gitattributes[5]).
++
+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.
 
 ifndef::git-format-patch[]
 ifndef::git-log[]
diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh
index 8c574221b2..0ffc845cdd 100755
--- a/t/t4015-diff-whitespace.sh
+++ b/t/t4015-diff-whitespace.sh
@@ -2133,7 +2133,7 @@ test_expect_success 'combine --ignore-blank-lines with --function-context 2' '
 		--ignore-blank-lines --function-context a b >actual.raw &&
 	sed -n "/@@/,\$p" <actual.raw >actual &&
 	cat <<-\EOF >expect &&
-	@@ -5,11 +6,9 @@ c
+	@@ -5,11 +6,9 @@
 	 function
 	 1
 	 2
diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index 80f35c5e16..f3374abd98 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -91,6 +91,13 @@ test_diff_funcname () {
 		fi
 	' &&
 
+	test_expect_success "$desc -W" '
+		git diff -U0 -W "$what" >W-U0-diff &&
+		echo >W-U0-expected &&
+		last_diff_context_line W-U0-diff >W-U0-actual &&
+		test_cmp W-U0-expected W-U0-actual
+	' &&
+
 	test_expect_success "$desc (accumulated)" '
 		git diff -U1 "$what".acc >diff &&
 		last_diff_context_line diff >actual.lines &&
diff --git a/xdiff/xemit.c b/xdiff/xemit.c
index 9d7d6c5087..02b5dbcc70 100644
--- a/xdiff/xemit.c
+++ b/xdiff/xemit.c
@@ -274,7 +274,9 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
 		 */
 
 		if (xecfg->flags & XDL_EMIT_FUNCNAMES) {
-			get_func_line(xe, xecfg, &func_line,
+			get_func_line(xe, xecfg,
+				      xecfg->flags & XDL_EMIT_FUNCCONTEXT
+				      ? NULL : &func_line,
 				      s1 - 1, funclineprev);
 			funclineprev = s1 - 1;
 		}
-- 
2.30.0.284.gd98b1dd5eaa7


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

* [PATCH 2/2] diff: test and document -W interaction with -U<n>
  2021-02-15 15:44         ` [PATCH v2 00/27] " Ævar Arnfjörð Bjarmason
  2021-02-15 15:50           ` [PATCH 0/2] diff: do not display hunk context under -W Ævar Arnfjörð Bjarmason
  2021-02-15 15:50           ` [PATCH 1/2] " Ævar Arnfjörð Bjarmason
@ 2021-02-15 15:50           ` Ævar Arnfjörð Bjarmason
  2021-02-16  7:26             ` Johannes Sixt
  2021-02-15 17:45           ` [PATCH v2 00/27] userdiff: refactor + test + doc + misc improvements Eric Sunshine
                             ` (37 subsequent siblings)
  40 siblings, 1 reply; 154+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-02-15 15:50 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, René Scharfe, Vegard Nossum, Jeff King,
	Ævar Arnfjörð Bjarmason

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 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.
+
 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.
 
 ifndef::git-format-patch[]
 ifndef::git-log[]
diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index f3374abd98..38dc029917 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -98,6 +98,11 @@ test_diff_funcname () {
 		test_cmp W-U0-expected W-U0-actual
 	' &&
 
+	test_expect_success "$desc -W interaction with -U<n>" '
+		git diff -U9001 "$what" >W-U9001-diff &&
+		grep "^@@ -1," W-U9001-diff
+	' &&
+
 	test_expect_success "$desc (accumulated)" '
 		git diff -U1 "$what".acc >diff &&
 		last_diff_context_line diff >actual.lines &&
-- 
2.30.0.284.gd98b1dd5eaa7


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

* Re: [PATCH v2 09/27] userdiff tests: match full hunk headers
  2021-02-15 15:44         ` [PATCH v2 09/27] userdiff tests: match full hunk headers Ævar Arnfjörð Bjarmason
@ 2021-02-15 17:13           ` Johannes Sixt
  2021-02-15 18:48             ` Ævar Arnfjörð Bjarmason
  2021-02-16 18:32             ` Junio C Hamano
  0 siblings, 2 replies; 154+ messages in thread
From: Johannes Sixt @ 2021-02-15 17:13 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: Junio C Hamano, git, Jeff King, Jonathan Nieder, Philippe Blain,
	Adam Spiers, Eric Sunshine, Chris Torek

Am 15.02.21 um 16:44 schrieb Ævar Arnfjörð Bjarmason:
> 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 adding corresponding *.ctx
> files, this has the added advantage that we're doing a "test_cmp", so
> when we have failures it's just a non-zero exit code from "grep",
> we'll actually have something meaningful in the "-v" output.
> 
> As we'll see in a follow-up commit this is an intermediate step
> towards even better test coverage. I'm structuring these tests in such
> a way as to benefit from the diff.colorMove detection.
> 
> The "sed -n -e" here was originally a single 's/^.*@@\( \|$\)//p'
> pattern, but the '\( \|$\)' part had portability issues on OSX and
> AIX.
> 
> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
>   t/t4018-diff-funcname.sh                      |  7 +++---
>   t/t4018/README                                | 22 +++++++++----------
>   t/t4018/README.ctx                            |  1 +
>   t/t4018/bash-arithmetic-function.ctx          |  1 +
>   t/t4018/bash-bashism-style-compact.ctx        |  1 +
>   [...and so on...]

This is what I meant by "without burdening test writers with lots of
subtleties".

I'm not a friend of this change :-(

I think you are going overboard with required test precision. To have 
useful tests for userdiff patterns that demonstrate its features, 
authors should write *many* tests. The right balance should be on the 
coverage of userdiff pattern features, not on the subtle details of each 
and everyone of it. Requiring that many additional context files makes 
it *really hard* to comply.

-- Hannes

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

* Re: [PATCH v2 11/27] blame tests: simplify userdiff driver test
  2021-02-15 15:44         ` [PATCH v2 11/27] blame tests: simplify userdiff driver test Ævar Arnfjörð Bjarmason
@ 2021-02-15 17:23           ` Johannes Sixt
  2021-02-17  1:33             ` Ævar Arnfjörð Bjarmason
  0 siblings, 1 reply; 154+ messages in thread
From: Johannes Sixt @ 2021-02-15 17:23 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: Junio C Hamano, git, Jeff King, Jonathan Nieder, Philippe Blain,
	Adam Spiers, Eric Sunshine, Chris Torek

Am 15.02.21 um 16:44 schrieb Ævar Arnfjörð Bjarmason:
> 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 <avarab@gmail.com>
> ---
>   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.

Please place "$fortran_file" in dquotes on the check_count line.

-- Hannes

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

* Re: [PATCH v2 00/27] userdiff: refactor + test + doc + misc improvements
  2021-02-15 15:44         ` [PATCH v2 00/27] " Ævar Arnfjörð Bjarmason
                             ` (2 preceding siblings ...)
  2021-02-15 15:50           ` [PATCH 2/2] diff: test and document -W interaction with -U<n> Ævar Arnfjörð Bjarmason
@ 2021-02-15 17:45           ` Eric Sunshine
  2021-02-15 20:03           ` Johannes Sixt
                             ` (36 subsequent siblings)
  40 siblings, 0 replies; 154+ messages in thread
From: Eric Sunshine @ 2021-02-15 17:45 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: Git List, Junio C Hamano, Johannes Sixt, Jeff King,
	Jonathan Nieder, Philippe Blain, Adam Spiers, Chris Torek

On Mon, Feb 15, 2021 at 10:44 AM Ævar Arnfjörð Bjarmason
<avarab@gmail.com> wrote:
> Incorporates all the feedback on v2 and more, see the range-diff
> below.

I hadn't finished reading the previous version of this series...
Nevertheless, see a few issues below which I noticed while scanning
the range-diff...

>     ++expect to appear in the hunk header. We munged away the starting "@@
>     ++[...] @@" part of the line for ease of not having to hardcode the line
>     ++numbers and offsets.

Nit: "munge" is an oddball word to use here. "We strip away the
starting..." would be simpler, but perhaps it doesn't matter too much
as this documentation is aimed at developers, not end users.

>     ++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.
>      +
>     -+You still need to enable built-in patterns with the the attribute
>     -+mechanism, via `.gitattributes`).
>     ++Nevertheless, you need to enable built-in patterns via .gitattributes`
>     ++for the pattern to take effect.

Missing opening backtick on `.gitattributes`.

>     ++Patterns in in a list of multiple 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.

s/in in/in/

Also, "of multiple" what?

>     ++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 "[!]".

Overall, I find this description easier to read and understand than in
the previous version.

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

* Re: [PATCH v2 12/27] userdiff tests: rewrite hunk header test infrastructure
  2021-02-15 15:44         ` [PATCH v2 12/27] userdiff tests: rewrite hunk header test infrastructure Ævar Arnfjörð Bjarmason
@ 2021-02-15 17:53           ` Johannes Sixt
  2021-02-15 20:06             ` Ævar Arnfjörð Bjarmason
  2021-02-16 18:35           ` Junio C Hamano
  1 sibling, 1 reply; 154+ messages in thread
From: Johannes Sixt @ 2021-02-15 17:53 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: Junio C Hamano, git, Jeff King, Jonathan Nieder, Philippe Blain,
	Adam Spiers, Eric Sunshine, Chris Torek

Am 15.02.21 um 16:44 schrieb Ævar Ar