{"thread":{"id":"55144","subject":"[PATCH] userdiff: add support for Emacs Lisp","startedAt":"2021-02-13T19:32:21Z","lastAt":"2021-04-10T12:30:15Z","messageCount":192,"participants":["Adam Spiers","Ævar Arnfjörð Bjarmason","Johannes Sixt","Eric Sunshine","Chris Torek","René Scharfe.","Junio C Hamano","Protesilaos Stavrou","Jeff King"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"416845","messageId":"20210213192447.6114-1-git@adamspiers.org","threadId":"55144","inReplyTo":null,"subject":"[PATCH] userdiff: add support for Emacs Lisp","fromName":"Adam Spiers","fromEmail":"git@adamspiers.org","sentAt":"2021-02-13T19:24:47Z","receivedAt":"2021-02-13T19:32:21Z","isPatch":true,"sender":{"key":"git@adamspiers.org","avatar":"https://avatars.githubusercontent.com/u/100738?v=4"},"body":"Add a diff driver which recognises Elisp top-level forms and outline\nheadings for display in hunk headers, and which correctly renders word\ndiffs.\n\nThis approach was previously discussed on the emacs-devel mailing list:\n\n   https://lists.gnu.org/archive/html/emacs-devel/2021-02/msg00705.html\n\n* userdiff.c (builtin_drivers): Provide regexen for Elisp top level\n  forms and outline headings, and for word diffs.\n* t/t4018-diff-funcname.sh (diffpatterns): Add test for elisp driver\n* t/t4018/elisp-outline-heading: Test fixture for outline headings\n* t/t4018/elisp-top-level-form: Test fixture for top level forms\n* t/t4034-diff-words.sh: Add test for elisp driver\n* t/t4034/elisp/*: Test fixtures for word diffs\n\nSigned-off-by: Protesilaos Stavrou <info@protesilaos.com>\nSigned-off-by: Adam Spiers <git@adamspiers.org>\n---\n Documentation/gitattributes.txt | 2 ++\n t/t4018-diff-funcname.sh        | 1 +\n t/t4018/elisp-outline-heading   | 6 ++++++\n t/t4018/elisp-top-level-form    | 7 +++++++\n t/t4034-diff-words.sh           | 1 +\n t/t4034/elisp/expect            | 9 +++++++++\n t/t4034/elisp/post              | 4 ++++\n t/t4034/elisp/pre               | 4 ++++\n userdiff.c                      | 9 +++++++++\n 9 files changed, 43 insertions(+)\n create mode 100644 t/t4018/elisp-outline-heading\n create mode 100644 t/t4018/elisp-top-level-form\n create mode 100644 t/t4034/elisp/expect\n create mode 100644 t/t4034/elisp/post\n create mode 100644 t/t4034/elisp/pre\n\ndiff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\nindex e84e104f93..0026055f99 100644\n--- a/Documentation/gitattributes.txt\n+++ b/Documentation/gitattributes.txt\n@@ -815,6 +815,8 @@ patterns are available:\n \n - `dts` suitable for devicetree (DTS) files.\n \n+- `elisp` suitable for source code in the Emacs Lisp language.\n+\n - `elixir` suitable for source code in the Elixir language.\n \n - `fortran` suitable for source code in the Fortran language.\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex 9675bc17db..a9ea2d3cd5 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -33,6 +33,7 @@ diffpatterns=\"\n \tcsharp\n \tcss\n \tdts\n+\telisp\n \telixir\n \tfortran\n \tfountain\ndiff --git a/t/t4018/elisp-outline-heading b/t/t4018/elisp-outline-heading\nnew file mode 100644\nindex 0000000000..c13bdafafe\n--- /dev/null\n+++ b/t/t4018/elisp-outline-heading\n@@ -0,0 +1,6 @@\n+;;; A top-level outline heading\n+;;;; A second-level outline heading RIGHT\n+\n+;; This is a ChangeMe comment outside top-level forms\n+(defun foo ()\n+  (bar 1 2 3)\ndiff --git a/t/t4018/elisp-top-level-form b/t/t4018/elisp-top-level-form\nnew file mode 100644\nindex 0000000000..683f7ffcf1\n--- /dev/null\n+++ b/t/t4018/elisp-top-level-form\n@@ -0,0 +1,7 @@\n+;;; Outline heading\n+\n+;; This is a comment\n+(RIGHT\n+  (list 1 2 3)\n+  ChangeMe\n+  (list a b c))\ndiff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh\nindex 0c8fb39ced..a546ee831a 100755\n--- a/t/t4034-diff-words.sh\n+++ b/t/t4034-diff-words.sh\n@@ -315,6 +315,7 @@ test_language_driver cpp\n test_language_driver csharp\n test_language_driver css\n test_language_driver dts\n+test_language_driver elisp\n test_language_driver fortran\n test_language_driver html\n test_language_driver java\ndiff --git a/t/t4034/elisp/expect b/t/t4034/elisp/expect\nnew file mode 100644\nindex 0000000000..29a6ef2520\n--- /dev/null\n+++ b/t/t4034/elisp/expect\n@@ -0,0 +1,9 @@\n+<BOLD>diff --git a/pre b/post<RESET>\n+<BOLD>index 4a39df8..6619e96 100644<RESET>\n+<BOLD>--- a/pre<RESET>\n+<BOLD>+++ b/post<RESET>\n+<CYAN>@@ -1,4 +1,4 @@<RESET>\n+(defun <RED>myfunc<RESET><GREEN>my-func<RESET> (<RED>a b<RESET><GREEN>first second<RESET>)\n+  \"This is a <RED>really<RESET><GREEN>(moderately)<RESET> cool function.\"\n+  (let ((c (<RED>+ a b<RESET><GREEN>1+ first<RESET>)))\n+    (format \"one more than the total is %d\" (<RED>1+<RESET><GREEN>+<RESET> c <GREEN>second<RESET>))))\ndiff --git a/t/t4034/elisp/post b/t/t4034/elisp/post\nnew file mode 100644\nindex 0000000000..6619e96657\n--- /dev/null\n+++ b/t/t4034/elisp/post\n@@ -0,0 +1,4 @@\n+(defun my-func (first second)\n+  \"This is a (moderately) cool function.\"\n+  (let ((c (1+ first)))\n+    (format \"one more than the total is %d\" (+ c second))))\ndiff --git a/t/t4034/elisp/pre b/t/t4034/elisp/pre\nnew file mode 100644\nindex 0000000000..4a39df8ffb\n--- /dev/null\n+++ b/t/t4034/elisp/pre\n@@ -0,0 +1,4 @@\n+(defun myfunc (a b)\n+  \"This is a really cool function.\"\n+  (let ((c (+ a b)))\n+    (format \"one more than the total is %d\" (1+ c))))\ndiff --git a/userdiff.c b/userdiff.c\nindex 3f81a2261c..292e51674a 100644\n--- a/userdiff.c\n+++ b/userdiff.c\n@@ -53,6 +53,15 @@ PATTERNS(\"dts\",\n \t /* Property names and math operators */\n \t \"[a-zA-Z0-9,._+?#-]+\"\n \t \"|[-+*/%&^|!~]|>>|<<|&&|\\\\|\\\\|\"),\n+PATTERNS(\"elisp\",\n+\t /* Top level forms and outline headings */\n+\t \"^((\\\\(|;;;+ +).+)\",\n+\t /*\n+\t  * Emacs Lisp allows symbol names containing any characters.\n+\t  * However spaces within the symbol must be escaped.\n+\t  */\n+\t \"(\\\\.|[^ ()])+\"\n+\t ),\n PATTERNS(\"elixir\",\n \t \"^[ \\t]*((def(macro|module|impl|protocol|p)?|test)[ \\t].*)$\",\n \t /* -- */\n-- \n2.30.1.490.ge54fde04c8.dirty\n\n"},{"id":"416850","messageId":"87wnvbbf2y.fsf@evledraar.gmail.com","threadId":"55144","inReplyTo":"20210213192447.6114-1-git@adamspiers.org","subject":"Re: [PATCH] userdiff: add support for Emacs Lisp","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-14T01:41:57Z","receivedAt":"2021-02-14T01:58:38Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Sat, Feb 13 2021, Adam Spiers wrote:\n\n> Add a diff driver which recognises Elisp top-level forms and outline\n> headings for display in hunk headers, and which correctly renders word\n> diffs.\n\nNeat, I for one would find this useful.\n\n> This approach was previously discussed on the emacs-devel mailing list:\n>\n>    https://lists.gnu.org/archive/html/emacs-devel/2021-02/msg00705.html\n>\n> * userdiff.c (builtin_drivers): Provide regexen for Elisp top level\n>   forms and outline headings, and for word diffs.\n> * t/t4018-diff-funcname.sh (diffpatterns): Add test for elisp driver\n> * t/t4018/elisp-outline-heading: Test fixture for outline headings\n> * t/t4018/elisp-top-level-form: Test fixture for top level forms\n> * t/t4034-diff-words.sh: Add test for elisp driver\n> * t/t4034/elisp/*: Test fixtures for word diffs\n\nPlease no on the overly verbose emacs.git convention of per-file\nchangelogs in commit messages :)\n\n> diff --git a/t/t4018/elisp-outline-heading b/t/t4018/elisp-outline-heading\n> new file mode 100644\n> index 0000000000..c13bdafafe\n> --- /dev/null\n> +++ b/t/t4018/elisp-outline-heading\n> @@ -0,0 +1,6 @@\n> +;;; A top-level outline heading\n> +;;;; A second-level outline heading RIGHT\n> +\n> +;; This is a ChangeMe comment outside top-level forms\n> +(defun foo ()\n> +  (bar 1 2 3)\n> diff --git a/t/t4018/elisp-top-level-form b/t/t4018/elisp-top-level-form\n> new file mode 100644\n> index 0000000000..683f7ffcf1\n> --- /dev/null\n> +++ b/t/t4018/elisp-top-level-form\n> @@ -0,0 +1,7 @@\n> +;;; Outline heading\n> +\n> +;; This is a comment\n> +(RIGHT\n> +  (list 1 2 3)\n> +  ChangeMe\n> +  (list a b c))\n> diff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh\n> index 0c8fb39ced..a546ee831a 100755\n> --- a/t/t4034-diff-words.sh\n> +++ b/t/t4034-diff-words.sh\n> @@ -315,6 +315,7 @@ test_language_driver cpp\n>  test_language_driver csharp\n>  test_language_driver css\n>  test_language_driver dts\n> +test_language_driver elisp\n>  test_language_driver fortran\n>  test_language_driver html\n>  test_language_driver java\n> diff --git a/t/t4034/elisp/expect b/t/t4034/elisp/expect\n> new file mode 100644\n> index 0000000000..29a6ef2520\n> --- /dev/null\n> +++ b/t/t4034/elisp/expect\n> @@ -0,0 +1,9 @@\n> +<BOLD>diff --git a/pre b/post<RESET>\n> +<BOLD>index 4a39df8..6619e96 100644<RESET>\n> +<BOLD>--- a/pre<RESET>\n> +<BOLD>+++ b/post<RESET>\n> +<CYAN>@@ -1,4 +1,4 @@<RESET>\n> +(defun <RED>myfunc<RESET><GREEN>my-func<RESET> (<RED>a b<RESET><GREEN>first second<RESET>)\n> +  \"This is a <RED>really<RESET><GREEN>(moderately)<RESET> cool function.\"\n> +  (let ((c (<RED>+ a b<RESET><GREEN>1+ first<RESET>)))\n> +    (format \"one more than the total is %d\" (<RED>1+<RESET><GREEN>+<RESET> c <GREEN>second<RESET>))))\n> diff --git a/t/t4034/elisp/post b/t/t4034/elisp/post\n> new file mode 100644\n> index 0000000000..6619e96657\n> --- /dev/null\n> +++ b/t/t4034/elisp/post\n> @@ -0,0 +1,4 @@\n> +(defun my-func (first second)\n> +  \"This is a (moderately) cool function.\"\n> +  (let ((c (1+ first)))\n> +    (format \"one more than the total is %d\" (+ c second))))\n> diff --git a/t/t4034/elisp/pre b/t/t4034/elisp/pre\n> new file mode 100644\n> index 0000000000..4a39df8ffb\n> --- /dev/null\n> +++ b/t/t4034/elisp/pre\n> @@ -0,0 +1,4 @@\n> +(defun myfunc (a b)\n> +  \"This is a really cool function.\"\n> +  (let ((c (+ a b)))\n> +    (format \"one more than the total is %d\" (1+ c))))\n> diff --git a/userdiff.c b/userdiff.c\n> index 3f81a2261c..292e51674a 100644\n> --- a/userdiff.c\n> +++ b/userdiff.c\n> @@ -53,6 +53,15 @@ PATTERNS(\"dts\",\n>  \t /* Property names and math operators */\n>  \t \"[a-zA-Z0-9,._+?#-]+\"\n>  \t \"|[-+*/%&^|!~]|>>|<<|&&|\\\\|\\\\|\"),\n> +PATTERNS(\"elisp\",\n> +\t /* Top level forms and outline headings */\n> +\t \"^((\\\\(|;;;+ +).+)\",\n> +\t /*\n> +\t  * Emacs Lisp allows symbol names containing any characters.\n> +\t  * However spaces within the symbol must be escaped.\n> +\t  */\n> +\t \"(\\\\.|[^ ()])+\"\n> +\t ),\n>  PATTERNS(\"elixir\",\n>  \t \"^[ \\t]*((def(macro|module|impl|protocol|p)?|test)[ \\t].*)$\",\n>  \t /* -- */\n\nI think this patch would benefit from first being dogfooded in the\nemacs-devel community. There's an existing regex in emacs.git's\nautoconf.sh which anyone developing emacs.git is using.\n\nIf you run:\n\n    diff -u <(git -c diff.elisp.xfuncname='^\\(def[^[:space:]]+[[:space:]]+([^()[:space:]]+)' log -p -- lisp | grep -m 100 @@) \\\n\t    <(git -c diff.elisp.xfuncname='^((\\(|;;;+ +).*)$' log -p -- lisp | grep -m 100 @@) \\\n    | less\n\nYou can see how it differs from yours, and that also neatly answers the\nquestion[1] you had in the linked thread about why these patterns tend\nto be explicitly scoped to how you define a function/package/whatever in\nthe language in question.\n\nJust a cursory \"git log -p -- lisp\" in emacs.git with your patch shows\ne.g. lisp/thingatpt.el where forms in a \"defun\" aren't indented (before\nit selects the \"defun\", after with yours it's a \"put\" in the middle of\nthe function).\n\nYours also changes it from e.g.:\n\n    @@ -61,7 +61,7 @@ forward-thing\n\nto:\n\n    @@ -61,7 +61,7 @@ (defun forward-thing (thing &optional n)\n\nIs this really desired in elisp? I also note how our tests in\nt4018-diff-funcname.sh are really bad in not testing for this at\nall. I.e. we just test that we match the right line, not how we extract\na match from it.\n\n1. https://lists.gnu.org/archive/html/emacs-devel/2021-02/msg00739.html\n"},{"id":"416877","messageId":"aab77ef2-c619-ed87-6c3b-9a1b5ec36f41@kdbg.org","threadId":"55144","inReplyTo":"87wnvbbf2y.fsf@evledraar.gmail.com","subject":"Re: [PATCH] userdiff: add support for Emacs Lisp","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2021-02-14T08:12:12Z","receivedAt":"2021-02-14T08:18:25Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 14.02.21 um 02:41 schrieb Ævar Arnfjörð Bjarmason:\n> Just a cursory \"git log -p -- lisp\" in emacs.git with your patch shows\n> e.g. lisp/thingatpt.el where forms in a \"defun\" aren't indented (before\n> it selects the \"defun\", after with yours it's a \"put\" in the middle of\n> the function).\n\nNote that negative matches can be specified. We use the feature in the \ncpp case to exclude public:/protected:/private: and label: that happen \nto be not indented. Perhaps that can be useful here?\n\nOh, and BTW, what the patterns treat as \"function\" must not match what \nthe language treats as function. The purpose of the hunk header is to \nspot a place in the source file easily. So, it does not hurt if \neval-and-compile forms are captured (as was mentioned in the linked \nthread) if desired.\n\n> \n> Yours also changes it from e.g.:\n> \n>      @@ -61,7 +61,7 @@ forward-thing\n> \n> to:\n> \n>      @@ -61,7 +61,7 @@ (defun forward-thing (thing &optional n)\n> \n> Is this really desired in elisp?\n\nIt's common practice to extract the entire line (sans indentation if \napplicable), not just the function name. Why would somebody want the \nlatter? It doesn't carry as much information as could be possible.\n\n> I also note how our tests in\n> t4018-diff-funcname.sh are really bad in not testing for this at\n> all. I.e. we just test that we match the right line, not how we extract\n> a match from it.\n\nOh, well. These are \"semi-automated\" tests cases. If you have an idea \nhow to achieve the goal without burdening test writers with lots of \nsubtleties, be my guest. ;)\n\n> \n> 1. https://lists.gnu.org/archive/html/emacs-devel/2021-02/msg00739.html\n> \n\n-- Hannes\n"},{"id":"416883","messageId":"b480dc67-6fea-4d4b-8f81-b9d4273e82ea@kdbg.org","threadId":"55144","inReplyTo":"aab77ef2-c619-ed87-6c3b-9a1b5ec36f41@kdbg.org","subject":"Re: [PATCH] userdiff: add support for Emacs Lisp","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2021-02-14T11:10:17Z","receivedAt":"2021-02-14T11:10:55Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 14.02.21 um 09:12 schrieb Johannes Sixt:\n> Am 14.02.21 um 02:41 schrieb Ævar Arnfjörð Bjarmason:\n>> Just a cursory \"git log -p -- lisp\" in emacs.git with your patch shows\n>> e.g. lisp/thingatpt.el where forms in a \"defun\" aren't indented (before\n>> it selects the \"defun\", after with yours it's a \"put\" in the middle of\n>> the function).\n> \n> Note that negative matches can be specified. We use the feature in the \n> cpp case to exclude public:/protected:/private: and label: that happen \n> to be not indented. Perhaps that can be useful here?\n> \n> Oh, and BTW, what the patterns treat as \"function\" must not match what \n\nI meant to say \"need not match\".\n\n> the language treats as function. The purpose of the hunk header is to \n> spot a place in the source file easily. So, it does not hurt if \n> eval-and-compile forms are captured (as was mentioned in the linked \n> thread) if desired.\n> \n>>\n>> Yours also changes it from e.g.:\n>>\n>>      @@ -61,7 +61,7 @@ forward-thing\n>>\n>> to:\n>>\n>>      @@ -61,7 +61,7 @@ (defun forward-thing (thing &optional n)\n>>\n>> Is this really desired in elisp?\n> \n> It's common practice to extract the entire line (sans indentation if \n> applicable), not just the function name. Why would somebody want the \n> latter? It doesn't carry as much information as could be possible.\n> \n>> I also note how our tests in\n>> t4018-diff-funcname.sh are really bad in not testing for this at\n>> all. I.e. we just test that we match the right line, not how we extract\n>> a match from it.\n> \n> Oh, well. These are \"semi-automated\" tests cases. If you have an idea \n> how to achieve the goal without burdening test writers with lots of \n> subtleties, be my guest. ;)\n> \n>>\n>> 1. https://lists.gnu.org/archive/html/emacs-devel/2021-02/msg00739.html\n>>\n> \n> -- Hannes\n\n"},{"id":"416885","messageId":"87tuqebj6m.fsf@evledraar.gmail.com","threadId":"55144","inReplyTo":"aab77ef2-c619-ed87-6c3b-9a1b5ec36f41@kdbg.org","subject":"Re: [PATCH] userdiff: add support for Emacs Lisp","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-14T18:25:37Z","receivedAt":"2021-02-14T18:26:25Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Sun, Feb 14 2021, Johannes Sixt wrote:\n\n> Am 14.02.21 um 02:41 schrieb Ævar Arnfjörð Bjarmason:\n>> Just a cursory \"git log -p -- lisp\" in emacs.git with your patch shows\n>> e.g. lisp/thingatpt.el where forms in a \"defun\" aren't indented (before\n>> it selects the \"defun\", after with yours it's a \"put\" in the middle of\n>> the function).\n>\n> Note that negative matches can be specified. We use the feature in the\n> cpp case to exclude public:/protected:/private: and label: that happen \n> to be not indented. Perhaps that can be useful here?\n>\n> Oh, and BTW, what the patterns treat as \"function\" must not match what\n> the language treats as function. The purpose of the hunk header is to \n> spot a place in the source file easily. So, it does not hurt if\n> eval-and-compile forms are captured (as was mentioned in the linked \n> thread) if desired.\n\nRight, so having lots of test-case is helpful, e.g. for elisp maybe you\nhave a top-level defun, maybe not, maybe the top-level is a \"(progn\" so\nyou'd like a second-level more meaningful context, or maybe not...\n\nObviously these userdiff patterns aren't a general parser and will\nalways be hit-and-miss, it's just useful to at least eyeball them\nagainst in-the-wild test data to check their sanity & add some tests.\n\nMy cursory glance at the emacs.git version v.s. what's being submitted\nhere is that this one does a worse job in *very common* cases.\n\n>> Yours also changes it from e.g.:\n>>      @@ -61,7 +61,7 @@ forward-thing\n>> to:\n>>      @@ -61,7 +61,7 @@ (defun forward-thing (thing &optional n)\n>> Is this really desired in elisp?\n>\n> It's common practice to extract the entire line (sans indentation if\n> applicable), not just the function name. Why would somebody want the \n> latter? It doesn't carry as much information as could be possible.\n\nBecause I'm familiar with the codebase I'm editing and I just need to\nknow that the diff I'm viewing is on the \"main\" function, not that it's\n\"main()\", \"int main(int argv, char **argv)\", \"int main(const int argv,\nconst char *argv[])\", or to either have a \" {\" or not at the end\ndepending on the project's coding style.\n\nI know our own userdiff builtin patterns don't do this, but it would be\nvery useful to retrofit this capability / maybe make it a configurable\nfeature, i.e. have them capture the meaningful part of the line, and you\ncould either print it all, or just that part.\n\n>> I also note how our tests in\n>> t4018-diff-funcname.sh are really bad in not testing for this at\n>> all. I.e. we just test that we match the right line, not how we extract\n>> a match from it.\n>\n> Oh, well. These are \"semi-automated\" tests cases. If you have an idea\n> how to achieve the goal without burdening test writers with lots of \n> subtleties, be my guest. ;)\n\nI'll do that soon.\n\n>> 1. https://lists.gnu.org/archive/html/emacs-devel/2021-02/msg00739.html\n>> \n>\n> -- Hannes\n\n"},{"id":"416887","messageId":"20210215005236.11313-2-avarab@gmail.com","threadId":"55144","inReplyTo":"87tuqebj6m.fsf@evledraar.gmail.com","subject":"[PATCH 01/20] userdiff: refactor away the parse_bool() function","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T00:52:17Z","receivedAt":"2021-02-15T00:54:01Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Since 6680a0874f (drop odd return value semantics from\nuserdiff_config, 2012-02-07) we have not cared about the return values\nof parse_tristate() or git_config_bool() v.s. falling through in\nuserdiff_config(), so let's do so in those cases to make the code\neasier to read.\n\nHaving a wrapper function for git_config_bool() dates back to\nd9bae1a178 (diff: cache textconv output, 2010-04-01) and\n122aa6f9c0 (diff: introduce diff.<driver>.binary, 2008-10-05), both of\nwhich predated the change in 6680a0874f which made their return values\nredundant.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n userdiff.c | 18 ++++++------------\n 1 file changed, 6 insertions(+), 12 deletions(-)\n\ndiff --git a/userdiff.c b/userdiff.c\nindex 3f81a2261c..c147bcbb17 100644\n--- a/userdiff.c\n+++ b/userdiff.c\n@@ -275,19 +275,12 @@ static int parse_funcname(struct userdiff_funcname *f, const char *k,\n \treturn 0;\n }\n \n-static int parse_tristate(int *b, const char *k, const char *v)\n+static void parse_tristate(int *b, const char *k, const char *v)\n {\n \tif (v && !strcasecmp(v, \"auto\"))\n \t\t*b = -1;\n \telse\n \t\t*b = git_config_bool(k, v);\n-\treturn 0;\n-}\n-\n-static int parse_bool(int *b, const char *k, const char *v)\n-{\n-\t*b = git_config_bool(k, v);\n-\treturn 0;\n }\n \n int userdiff_config(const char *k, const char *v)\n@@ -312,16 +305,17 @@ int userdiff_config(const char *k, const char *v)\n \t\treturn parse_funcname(&drv->funcname, k, v, 0);\n \tif (!strcmp(type, \"xfuncname\"))\n \t\treturn parse_funcname(&drv->funcname, k, v, REG_EXTENDED);\n-\tif (!strcmp(type, \"binary\"))\n-\t\treturn parse_tristate(&drv->binary, k, v);\n \tif (!strcmp(type, \"command\"))\n \t\treturn git_config_string(&drv->external, k, v);\n \tif (!strcmp(type, \"textconv\"))\n \t\treturn git_config_string(&drv->textconv, k, v);\n-\tif (!strcmp(type, \"cachetextconv\"))\n-\t\treturn parse_bool(&drv->textconv_want_cache, k, v);\n \tif (!strcmp(type, \"wordregex\"))\n \t\treturn git_config_string(&drv->word_regex, k, v);\n+\t/* Don't care about the parse errors for these, fallthrough */\n+\tif (!strcmp(type, \"cachetextconv\"))\n+\t\tdrv->textconv_want_cache = git_config_bool(k, v);\n+\tif (!strcmp(type, \"binary\"))\n+\t\tparse_tristate(&drv->binary, k, v);\n \n \treturn 0;\n }\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416888","messageId":"20210215005236.11313-4-avarab@gmail.com","threadId":"55144","inReplyTo":"87tuqebj6m.fsf@evledraar.gmail.com","subject":"[PATCH 03/20] userdiff: add and use for_each_userdiff_driver()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T00:52:19Z","receivedAt":"2021-02-15T00:54:01Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Refactor the userdiff_find_by_namelen() function so that a new\nfor_each_userdiff_driver() API function does most of the work.\n\nThis will be useful for the same reason we've got other for_each_*()\nAPI functions as part of various APIs, and will be used in a follow-up\ncommit.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n userdiff.c | 61 +++++++++++++++++++++++++++++++++++++++++++-----------\n userdiff.h | 15 ++++++++++++++\n 2 files changed, 64 insertions(+), 12 deletions(-)\n\ndiff --git a/userdiff.c b/userdiff.c\nindex c92cbcc054..92b5a97e12 100644\n--- a/userdiff.c\n+++ b/userdiff.c\n@@ -250,20 +250,32 @@ static struct userdiff_driver driver_false = {\n \t{ NULL, 0 }\n };\n \n-static struct userdiff_driver *userdiff_find_by_namelen(const char *k, size_t len)\n+struct for_each_userdiff_driver_cb {\n+\tconst char *k;\n+\tsize_t len;\n+\tstruct userdiff_driver *driver;\n+};\n+\n+static int userdiff_find_by_namelen_cb(struct userdiff_driver *driver,\n+\t\t\t\t       enum userdiff_driver_type type, void *priv)\n {\n-\tint i;\n-\tfor (i = 0; i < ndrivers; i++) {\n-\t\tstruct userdiff_driver *drv = drivers + i;\n-\t\tif (!strncmp(drv->name, k, len) && !drv->name[len])\n-\t\t\treturn drv;\n-\t}\n-\tfor (i = 0; i < ARRAY_SIZE(builtin_drivers); i++) {\n-\t\tstruct userdiff_driver *drv = builtin_drivers + i;\n-\t\tif (!strncmp(drv->name, k, len) && !drv->name[len])\n-\t\t\treturn drv;\n+\tstruct for_each_userdiff_driver_cb *cb_data = priv;\n+\n+\tif (!strncmp(driver->name, cb_data->k, cb_data->len) &&\n+\t    !driver->name[cb_data->len]) {\n+\t\tcb_data->driver = driver;\n+\t\treturn -1; /* found it! */\n \t}\n-\treturn NULL;\n+\treturn 0;\n+}\n+\n+static struct userdiff_driver *userdiff_find_by_namelen(const char *k, size_t len)\n+{\n+\tstruct for_each_userdiff_driver_cb udcbdata = { .k = k, .len = len, .driver = NULL };\n+\n+\tfor_each_userdiff_driver(userdiff_find_by_namelen_cb,\n+\t\t\t\t USERDIFF_DRIVER_TYPE_UNSPECIFIED, &udcbdata);\n+\treturn udcbdata.driver;\n }\n \n static int parse_funcname(struct userdiff_funcname *f, const char *k,\n@@ -364,3 +376,28 @@ struct userdiff_driver *userdiff_get_textconv(struct repository *r,\n \n \treturn driver;\n }\n+\n+int for_each_userdiff_driver(each_userdiff_driver_fn fn,\n+\t\t\t     enum userdiff_driver_type type, void *cb_data)\n+{\n+\tint i, ret;\n+\tif (type & (USERDIFF_DRIVER_TYPE_UNSPECIFIED | USERDIFF_DRIVER_TYPE_CUSTOM)) {\n+\n+\t\tfor (i = 0; i < ndrivers; i++) {\n+\t\t\tstruct userdiff_driver *drv = drivers + i;\n+\t\t\tret = fn(drv, USERDIFF_DRIVER_TYPE_CUSTOM, cb_data);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\t\t}\n+\t}\n+\tif (type & (USERDIFF_DRIVER_TYPE_UNSPECIFIED | USERDIFF_DRIVER_TYPE_BUILTIN)) {\n+\n+\t\tfor (i = 0; i < ARRAY_SIZE(builtin_drivers); i++) {\n+\t\t\tstruct userdiff_driver *drv = builtin_drivers + i;\n+\t\t\tret = fn(drv, USERDIFF_DRIVER_TYPE_BUILTIN, cb_data);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\t\t}\n+\t}\n+\treturn 0;\n+}\ndiff --git a/userdiff.h b/userdiff.h\nindex 203057e13e..fe14014a77 100644\n--- a/userdiff.h\n+++ b/userdiff.h\n@@ -21,6 +21,13 @@ struct userdiff_driver {\n \tstruct notes_cache *textconv_cache;\n \tint textconv_want_cache;\n };\n+enum userdiff_driver_type {\n+\tUSERDIFF_DRIVER_TYPE_UNSPECIFIED = 1<<0,\n+\tUSERDIFF_DRIVER_TYPE_BUILTIN = 1<<1,\n+\tUSERDIFF_DRIVER_TYPE_CUSTOM = 1<<2,\n+};\n+typedef int (*each_userdiff_driver_fn)(struct userdiff_driver *,\n+\t\t\t\t       enum userdiff_driver_type, void *);\n \n int userdiff_config(const char *k, const char *v);\n struct userdiff_driver *userdiff_find_by_name(const char *name);\n@@ -34,4 +41,12 @@ struct userdiff_driver *userdiff_find_by_path(struct index_state *istate,\n struct userdiff_driver *userdiff_get_textconv(struct repository *r,\n \t\t\t\t\t      struct userdiff_driver *driver);\n \n+/*\n+ * Iterate over each driver of type userdiff_driver_type, or\n+ * USERDIFF_DRIVER_TYPE_UNSPECIFIED for all of them. Return non-zero\n+ * to exit from the loop.\n+ */\n+int for_each_userdiff_driver(each_userdiff_driver_fn,\n+\t\t\t     enum userdiff_driver_type, void *);\n+\n #endif /* USERDIFF */\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416889","messageId":"20210215005236.11313-3-avarab@gmail.com","threadId":"55144","inReplyTo":"87tuqebj6m.fsf@evledraar.gmail.com","subject":"[PATCH 02/20] userdiff: re-order builtin drivers in alphabetical order","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T00:52:18Z","receivedAt":"2021-02-15T00:54:01Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Address some old code smell and move around the built-in userdiff\ndefinitions so they're both in alphabetical order, and now in the same\norder they appear in the gitattributes(5) documentation.\n\nThe two started drifting in be58e70dba (diff: unify external diff and\nfuncname parsing code, 2008-10-05), and then even further in\n80c49c3de2 (color-words: make regex configurable via attributes,\n2009-01-17) when the \"cpp\" pattern was added.\n\nThere are no functional changes here, and as --color-moved will show\nonly moved existing lines.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n userdiff.c | 76 +++++++++++++++++++++++++++---------------------------\n 1 file changed, 38 insertions(+), 38 deletions(-)\n\ndiff --git a/userdiff.c b/userdiff.c\nindex c147bcbb17..c92cbcc054 100644\n--- a/userdiff.c\n+++ b/userdiff.c\n@@ -44,6 +44,44 @@ PATTERNS(\"bash\",\n \t /* -- */\n \t /* Characters not in the default $IFS value */\n \t \"[^ \\t]+\"),\n+PATTERNS(\"bibtex\", \"(@[a-zA-Z]{1,}[ \\t]*\\\\{{0,1}[ \\t]*[^ \\t\\\"@',\\\\#}{~%]*).*$\",\n+\t \"[={}\\\"]|[^={}\\\" \\t]+\"),\n+PATTERNS(\"cpp\",\n+\t /* Jump targets or access declarations */\n+\t \"!^[ \\t]*[A-Za-z_][A-Za-z_0-9]*:[[:space:]]*($|/[/*])\\n\"\n+\t /* functions/methods, variables, and compounds at top level */\n+\t \"^((::[[:space:]]*)?[A-Za-z_].*)$\",\n+\t /* -- */\n+\t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n+\t \"|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lLuU]*\"\n+\t \"|[-+*/<>%&^|=!]=|--|\\\\+\\\\+|<<=?|>>=?|&&|\\\\|\\\\||::|->\\\\*?|\\\\.\\\\*\"),\n+PATTERNS(\"csharp\",\n+\t /* Keywords */\n+\t \"!^[ \\t]*(do|while|for|if|else|instanceof|new|return|switch|case|throw|catch|using)\\n\"\n+\t /* Methods and constructors */\n+\t \"^[ \\t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe|async)[ \\t]+)*[][<>@.~_[:alnum:]]+[ \\t]+[<>@._[:alnum:]]+[ \\t]*\\\\(.*\\\\))[ \\t]*$\\n\"\n+\t /* Properties */\n+\t \"^[ \\t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \\t]+)*[][<>@.~_[:alnum:]]+[ \\t]+[@._[:alnum:]]+)[ \\t]*$\\n\"\n+\t /* Type definitions */\n+\t \"^[ \\t]*(((static|public|internal|private|protected|new|unsafe|sealed|abstract|partial)[ \\t]+)*(class|enum|interface|struct)[ \\t]+.*)$\\n\"\n+\t /* Namespace */\n+\t \"^[ \\t]*(namespace[ \\t]+.*)$\",\n+\t /* -- */\n+\t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n+\t \"|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?\"\n+\t \"|[-+*/<>%&^|=!]=|--|\\\\+\\\\+|<<=?|>>=?|&&|\\\\|\\\\||::|->\"),\n+IPATTERN(\"css\",\n+\t \"![:;][[:space:]]*$\\n\"\n+\t \"^[:[@.#]?[_a-z0-9].*$\",\n+\t /* -- */\n+\t /*\n+\t  * This regex comes from W3C CSS specs. Should theoretically also\n+\t  * allow ISO 10646 characters U+00A0 and higher,\n+\t  * but they are not handled in this regex.\n+\t  */\n+\t \"-?[_a-zA-Z][-_a-zA-Z0-9]*\" /* identifiers */\n+\t \"|-?[0-9]+|\\\\#[0-9a-fA-F]+\" /* numbers */\n+),\n PATTERNS(\"dts\",\n \t \"!;\\n\"\n \t \"!=\\n\"\n@@ -191,46 +229,8 @@ PATTERNS(\"rust\",\n \t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n \t \"|[0-9][0-9_a-fA-Fiosuxz]*(\\\\.([0-9]*[eE][+-]?)?[0-9_fF]*)?\"\n \t \"|[-+*\\\\/<>%&^|=!:]=|<<=?|>>=?|&&|\\\\|\\\\||->|=>|\\\\.{2}=|\\\\.{3}|::\"),\n-PATTERNS(\"bibtex\", \"(@[a-zA-Z]{1,}[ \\t]*\\\\{{0,1}[ \\t]*[^ \\t\\\"@',\\\\#}{~%]*).*$\",\n-\t \"[={}\\\"]|[^={}\\\" \\t]+\"),\n PATTERNS(\"tex\", \"^(\\\\\\\\((sub)*section|chapter|part)\\\\*{0,1}\\\\{.*)$\",\n \t \"\\\\\\\\[a-zA-Z@]+|\\\\\\\\.|[a-zA-Z0-9\\x80-\\xff]+\"),\n-PATTERNS(\"cpp\",\n-\t /* Jump targets or access declarations */\n-\t \"!^[ \\t]*[A-Za-z_][A-Za-z_0-9]*:[[:space:]]*($|/[/*])\\n\"\n-\t /* functions/methods, variables, and compounds at top level */\n-\t \"^((::[[:space:]]*)?[A-Za-z_].*)$\",\n-\t /* -- */\n-\t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n-\t \"|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lLuU]*\"\n-\t \"|[-+*/<>%&^|=!]=|--|\\\\+\\\\+|<<=?|>>=?|&&|\\\\|\\\\||::|->\\\\*?|\\\\.\\\\*\"),\n-PATTERNS(\"csharp\",\n-\t /* Keywords */\n-\t \"!^[ \\t]*(do|while|for|if|else|instanceof|new|return|switch|case|throw|catch|using)\\n\"\n-\t /* Methods and constructors */\n-\t \"^[ \\t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe|async)[ \\t]+)*[][<>@.~_[:alnum:]]+[ \\t]+[<>@._[:alnum:]]+[ \\t]*\\\\(.*\\\\))[ \\t]*$\\n\"\n-\t /* Properties */\n-\t \"^[ \\t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \\t]+)*[][<>@.~_[:alnum:]]+[ \\t]+[@._[:alnum:]]+)[ \\t]*$\\n\"\n-\t /* Type definitions */\n-\t \"^[ \\t]*(((static|public|internal|private|protected|new|unsafe|sealed|abstract|partial)[ \\t]+)*(class|enum|interface|struct)[ \\t]+.*)$\\n\"\n-\t /* Namespace */\n-\t \"^[ \\t]*(namespace[ \\t]+.*)$\",\n-\t /* -- */\n-\t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n-\t \"|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?\"\n-\t \"|[-+*/<>%&^|=!]=|--|\\\\+\\\\+|<<=?|>>=?|&&|\\\\|\\\\||::|->\"),\n-IPATTERN(\"css\",\n-\t \"![:;][[:space:]]*$\\n\"\n-\t \"^[:[@.#]?[_a-z0-9].*$\",\n-\t /* -- */\n-\t /*\n-\t  * This regex comes from W3C CSS specs. Should theoretically also\n-\t  * allow ISO 10646 characters U+00A0 and higher,\n-\t  * but they are not handled in this regex.\n-\t  */\n-\t \"-?[_a-zA-Z][-_a-zA-Z0-9]*\" /* identifiers */\n-\t \"|-?[0-9]+|\\\\#[0-9a-fA-F]+\" /* numbers */\n-),\n { \"default\", NULL, -1, { NULL, 0 } },\n };\n #undef PATTERNS\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416890","messageId":"20210215005236.11313-1-avarab@gmail.com","threadId":"55144","inReplyTo":"87tuqebj6m.fsf@evledraar.gmail.com","subject":"[PATCH 00/20] userdiff: refactor + test + doc + misc improvements","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T00:52:16Z","receivedAt":"2021-02-15T00:54:01Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"This started out as a version of 07/20 since I noticed that we could\neasily break userdiff patterns in subtle ways and and not notice,\nsince nothing tested the full @@ line (just whether we had a \"RIGHT\"\ntoken).\n\nBut it grew into fixing warts in the code, tests docs, and I even\nmanaged to improve the golang userdiff driver while I was at it.\n\nHopefully this isn't too hard to review, the diffstat is scary, but\nit's mostly moving things around. I went out of my way to make 08/20\nas easy as possible to review with the color-move detection.\n\nÆvar Arnfjörð Bjarmason (20):\n  userdiff: refactor away the parse_bool() function\n  userdiff: re-order builtin drivers in alphabetical order\n  userdiff: add and use for_each_userdiff_driver()\n  userdiff tests: explicitly test \"default\" pattern\n  userdiff tests: list builtin drivers via test-tool\n  userdiff: remove support for \"broken\" tests\n  userdiff tests: match full hunk headers\n  userdiff tests: rewrite hunk header test infrastructure\n  blame tests: don't rely on t/t4018/ directory\n  userdiff tests: move custom patterns into one test file\n  userdiff tests: remove hack for \"RIGHT\" token\n  userdiff: match \"package\" in diff=golang\n  userdiff tests + docs: document & test \"diff.<driver>.x?funcname\"\n  gitattributes doc: reword discussion of built-in userdiff patterns\n  gitattributes doc: document multi-line userdiff patterns\n  userdiff tests: remove \"funcname\" from custom3 test\n  userdiff tests: factor out test_diff_funcname() logic\n  userdiff tests: test hunk headers on accumulated files\n  userdiff tests: test hunk header selection with -U0\n  userdiff tests: assert empty hunk header context on -U<large>\n\n Documentation/config/diff.txt              |  12 ++\n Documentation/gitattributes.txt            |  33 ++-\n Makefile                                   |   1 +\n t/annotate-tests.sh                        |  16 +-\n t/helper/test-tool.c                       |   1 +\n t/helper/test-tool.h                       |   1 +\n t/helper/test-userdiff.c                   |  32 +++\n t/t4018-diff-funcname.sh                   | 154 +++++++------\n t/t4018/README                             |  18 --\n t/t4018/bash-arithmetic-function           |   4 -\n t/t4018/bash-bashism-style-compact         |   6 -\n t/t4018/bash-bashism-style-function        |   4 -\n t/t4018/bash-bashism-style-whitespace      |   4 -\n t/t4018/bash-conditional-function          |   4 -\n t/t4018/bash-missing-parentheses           |   6 -\n t/t4018/bash-mixed-style-compact           |   4 -\n t/t4018/bash-mixed-style-function          |   4 -\n t/t4018/bash-nested-functions              |   6 -\n t/t4018/bash-other-characters              |   4 -\n t/t4018/bash-posix-style-compact           |   4 -\n t/t4018/bash-posix-style-function          |   4 -\n t/t4018/bash-posix-style-whitespace        |   4 -\n t/t4018/bash-subshell-function             |   4 -\n t/t4018/bash-trailing-comment              |   4 -\n t/t4018/bash.sh                            | 160 ++++++++++++++\n t/t4018/cpp-c++-function                   |   4 -\n t/t4018/cpp-class-constructor              |   4 -\n t/t4018/cpp-class-constructor-mem-init     |   5 -\n t/t4018/cpp-class-definition               |   4 -\n t/t4018/cpp-class-definition-derived       |   5 -\n t/t4018/cpp-class-destructor               |   4 -\n t/t4018/cpp-function-returning-global-type |   4 -\n t/t4018/cpp-function-returning-nested      |   5 -\n t/t4018/cpp-function-returning-pointer     |   4 -\n t/t4018/cpp-function-returning-reference   |   4 -\n t/t4018/cpp-gnu-style-function             |   5 -\n t/t4018/cpp-namespace-definition           |   4 -\n t/t4018/cpp-operator-definition            |   4 -\n t/t4018/cpp-skip-access-specifiers         |   8 -\n t/t4018/cpp-skip-comment-block             |   9 -\n t/t4018/cpp-skip-labels                    |   8 -\n t/t4018/cpp-struct-definition              |   9 -\n t/t4018/cpp-struct-single-line             |   7 -\n t/t4018/cpp-template-function-definition   |   4 -\n t/t4018/cpp-union-definition               |   4 -\n t/t4018/cpp-void-c-function                |   4 -\n t/t4018/cpp.sh                             | 240 +++++++++++++++++++++\n t/t4018/css-attribute-value-selector       |   4 -\n t/t4018/css-block-level-@-statements       |  10 -\n t/t4018/css-brace-in-col-1                 |   5 -\n t/t4018/css-class-selector                 |   4 -\n t/t4018/css-colon-eol                      |   4 -\n t/t4018/css-colon-selector                 |   5 -\n t/t4018/css-common                         |   4 -\n t/t4018/css-id-selector                    |   4 -\n t/t4018/css-long-selector-list             |   6 -\n t/t4018/css-prop-sans-indent               |   5 -\n t/t4018/css-root-selector                  |   4 -\n t/t4018/css-short-selector-list            |   4 -\n t/t4018/css-trailing-space                 |   5 -\n t/t4018/css.sh                             | 146 +++++++++++++\n t/t4018/custom.sh                          | 160 ++++++++++++++\n t/t4018/custom1-pattern                    |  17 --\n t/t4018/custom2-match-to-end-of-line       |   8 -\n t/t4018/custom3-alternation-in-pattern     |  17 --\n t/t4018/dts-labels                         |   9 -\n t/t4018/dts-node-unitless                  |   8 -\n t/t4018/dts-nodes                          |   8 -\n t/t4018/dts-nodes-boolean-prop             |   9 -\n t/t4018/dts-nodes-comment1                 |   8 -\n t/t4018/dts-nodes-comment2                 |   8 -\n t/t4018/dts-nodes-multiline-prop           |  13 --\n t/t4018/dts-reference                      |   9 -\n t/t4018/dts-root                           |   5 -\n t/t4018/dts-root-comment                   |   8 -\n t/t4018/dts.sh                             | 149 +++++++++++++\n t/t4018/elixir-do-not-pick-end             |   5 -\n t/t4018/elixir-ex-unit-test                |   6 -\n t/t4018/elixir-function                    |   5 -\n t/t4018/elixir-macro                       |   5 -\n t/t4018/elixir-module                      |   9 -\n t/t4018/elixir-module-func                 |   8 -\n t/t4018/elixir-nested-module               |   9 -\n t/t4018/elixir-private-function            |   5 -\n t/t4018/elixir-protocol                    |   6 -\n t/t4018/elixir-protocol-implementation     |   5 -\n t/t4018/elixir.sh                          | 127 +++++++++++\n t/t4018/fortran-block-data                 |   5 -\n t/t4018/fortran-comment                    |  13 --\n t/t4018/fortran-comment-keyword            |  14 --\n t/t4018/fortran-comment-legacy             |  13 --\n t/t4018/fortran-comment-legacy-star        |  13 --\n t/t4018/fortran-external-function          |   9 -\n t/t4018/fortran-external-subroutine        |   5 -\n t/t4018/fortran-module                     |   5 -\n t/t4018/fortran-module-procedure           |  13 --\n t/t4018/fortran-program                    |   5 -\n t/t4018/fortran.sh                         | 159 ++++++++++++++\n t/t4018/fountain-scene                     |   4 -\n t/t4018/fountain.sh                        |  14 ++\n t/t4018/golang-complex-function            |   8 -\n t/t4018/golang-func                        |   4 -\n t/t4018/golang-interface                   |   4 -\n t/t4018/golang-long-func                   |   5 -\n t/t4018/golang-struct                      |   4 -\n t/t4018/golang.sh                          |  69 ++++++\n t/t4018/java-class-member-function         |   8 -\n t/t4018/java.sh                            |  18 ++\n t/t4018/markdown-heading-indented          |   6 -\n t/t4018/markdown-heading-non-headings      |  17 --\n t/t4018/markdown.sh                        |  39 ++++\n t/t4018/matlab-class-definition            |   5 -\n t/t4018/matlab-function                    |   4 -\n t/t4018/matlab-octave-section-1            |   3 -\n t/t4018/matlab-octave-section-2            |   3 -\n t/t4018/matlab-section                     |   3 -\n t/t4018/matlab.sh                          |  55 +++++\n t/t4018/perl-skip-end-of-heredoc           |   8 -\n t/t4018/perl-skip-forward-decl             |  10 -\n t/t4018/perl-skip-sub-in-pod               |  18 --\n t/t4018/perl-sub-definition                |   4 -\n t/t4018/perl-sub-definition-kr-brace       |   4 -\n t/t4018/perl.sh                            |  78 +++++++\n t/t4018/php-abstract-class                 |   4 -\n t/t4018/php-abstract-method                |   7 -\n t/t4018/php-class                          |   4 -\n t/t4018/php-final-class                    |   4 -\n t/t4018/php-final-method                   |   7 -\n t/t4018/php-function                       |   4 -\n t/t4018/php-interface                      |   4 -\n t/t4018/php-method                         |   7 -\n t/t4018/php-trait                          |   7 -\n t/t4018/php.sh                             | 106 +++++++++\n t/t4018/python-async-def                   |   4 -\n t/t4018/python-class                       |   4 -\n t/t4018/python-def                         |   4 -\n t/t4018/python-indented-async-def          |   7 -\n t/t4018/python-indented-class              |   5 -\n t/t4018/python-indented-def                |   7 -\n t/t4018/python.sh                          |  71 ++++++\n t/t4018/rust-fn                            |   5 -\n t/t4018/rust-impl                          |   5 -\n t/t4018/rust-macro-rules                   |   6 -\n t/t4018/rust-struct                        |   5 -\n t/t4018/rust-trait                         |   5 -\n t/t4018/rust.sh                            |  60 ++++++\n userdiff.c                                 | 157 ++++++++------\n userdiff.h                                 |  15 ++\n 148 files changed, 1937 insertions(+), 905 deletions(-)\n create mode 100644 t/helper/test-userdiff.c\n delete mode 100644 t/t4018/README\n delete mode 100644 t/t4018/bash-arithmetic-function\n delete mode 100644 t/t4018/bash-bashism-style-compact\n delete mode 100644 t/t4018/bash-bashism-style-function\n delete mode 100644 t/t4018/bash-bashism-style-whitespace\n delete mode 100644 t/t4018/bash-conditional-function\n delete mode 100644 t/t4018/bash-missing-parentheses\n delete mode 100644 t/t4018/bash-mixed-style-compact\n delete mode 100644 t/t4018/bash-mixed-style-function\n delete mode 100644 t/t4018/bash-nested-functions\n delete mode 100644 t/t4018/bash-other-characters\n delete mode 100644 t/t4018/bash-posix-style-compact\n delete mode 100644 t/t4018/bash-posix-style-function\n delete mode 100644 t/t4018/bash-posix-style-whitespace\n delete mode 100644 t/t4018/bash-subshell-function\n delete mode 100644 t/t4018/bash-trailing-comment\n create mode 100755 t/t4018/bash.sh\n delete mode 100644 t/t4018/cpp-c++-function\n delete mode 100644 t/t4018/cpp-class-constructor\n delete mode 100644 t/t4018/cpp-class-constructor-mem-init\n delete mode 100644 t/t4018/cpp-class-definition\n delete mode 100644 t/t4018/cpp-class-definition-derived\n delete mode 100644 t/t4018/cpp-class-destructor\n delete mode 100644 t/t4018/cpp-function-returning-global-type\n delete mode 100644 t/t4018/cpp-function-returning-nested\n delete mode 100644 t/t4018/cpp-function-returning-pointer\n delete mode 100644 t/t4018/cpp-function-returning-reference\n delete mode 100644 t/t4018/cpp-gnu-style-function\n delete mode 100644 t/t4018/cpp-namespace-definition\n delete mode 100644 t/t4018/cpp-operator-definition\n delete mode 100644 t/t4018/cpp-skip-access-specifiers\n delete mode 100644 t/t4018/cpp-skip-comment-block\n delete mode 100644 t/t4018/cpp-skip-labels\n delete mode 100644 t/t4018/cpp-struct-definition\n delete mode 100644 t/t4018/cpp-struct-single-line\n delete mode 100644 t/t4018/cpp-template-function-definition\n delete mode 100644 t/t4018/cpp-union-definition\n delete mode 100644 t/t4018/cpp-void-c-function\n create mode 100755 t/t4018/cpp.sh\n delete mode 100644 t/t4018/css-attribute-value-selector\n delete mode 100644 t/t4018/css-block-level-@-statements\n delete mode 100644 t/t4018/css-brace-in-col-1\n delete mode 100644 t/t4018/css-class-selector\n delete mode 100644 t/t4018/css-colon-eol\n delete mode 100644 t/t4018/css-colon-selector\n delete mode 100644 t/t4018/css-common\n delete mode 100644 t/t4018/css-id-selector\n delete mode 100644 t/t4018/css-long-selector-list\n delete mode 100644 t/t4018/css-prop-sans-indent\n delete mode 100644 t/t4018/css-root-selector\n delete mode 100644 t/t4018/css-short-selector-list\n delete mode 100644 t/t4018/css-trailing-space\n create mode 100755 t/t4018/css.sh\n create mode 100755 t/t4018/custom.sh\n delete mode 100644 t/t4018/custom1-pattern\n delete mode 100644 t/t4018/custom2-match-to-end-of-line\n delete mode 100644 t/t4018/custom3-alternation-in-pattern\n delete mode 100644 t/t4018/dts-labels\n delete mode 100644 t/t4018/dts-node-unitless\n delete mode 100644 t/t4018/dts-nodes\n delete mode 100644 t/t4018/dts-nodes-boolean-prop\n delete mode 100644 t/t4018/dts-nodes-comment1\n delete mode 100644 t/t4018/dts-nodes-comment2\n delete mode 100644 t/t4018/dts-nodes-multiline-prop\n delete mode 100644 t/t4018/dts-reference\n delete mode 100644 t/t4018/dts-root\n delete mode 100644 t/t4018/dts-root-comment\n create mode 100755 t/t4018/dts.sh\n delete mode 100644 t/t4018/elixir-do-not-pick-end\n delete mode 100644 t/t4018/elixir-ex-unit-test\n delete mode 100644 t/t4018/elixir-function\n delete mode 100644 t/t4018/elixir-macro\n delete mode 100644 t/t4018/elixir-module\n delete mode 100644 t/t4018/elixir-module-func\n delete mode 100644 t/t4018/elixir-nested-module\n delete mode 100644 t/t4018/elixir-private-function\n delete mode 100644 t/t4018/elixir-protocol\n delete mode 100644 t/t4018/elixir-protocol-implementation\n create mode 100755 t/t4018/elixir.sh\n delete mode 100644 t/t4018/fortran-block-data\n delete mode 100644 t/t4018/fortran-comment\n delete mode 100644 t/t4018/fortran-comment-keyword\n delete mode 100644 t/t4018/fortran-comment-legacy\n delete mode 100644 t/t4018/fortran-comment-legacy-star\n delete mode 100644 t/t4018/fortran-external-function\n delete mode 100644 t/t4018/fortran-external-subroutine\n delete mode 100644 t/t4018/fortran-module\n delete mode 100644 t/t4018/fortran-module-procedure\n delete mode 100644 t/t4018/fortran-program\n create mode 100755 t/t4018/fortran.sh\n delete mode 100644 t/t4018/fountain-scene\n create mode 100755 t/t4018/fountain.sh\n delete mode 100644 t/t4018/golang-complex-function\n delete mode 100644 t/t4018/golang-func\n delete mode 100644 t/t4018/golang-interface\n delete mode 100644 t/t4018/golang-long-func\n delete mode 100644 t/t4018/golang-struct\n create mode 100755 t/t4018/golang.sh\n delete mode 100644 t/t4018/java-class-member-function\n create mode 100755 t/t4018/java.sh\n delete mode 100644 t/t4018/markdown-heading-indented\n delete mode 100644 t/t4018/markdown-heading-non-headings\n create mode 100755 t/t4018/markdown.sh\n delete mode 100644 t/t4018/matlab-class-definition\n delete mode 100644 t/t4018/matlab-function\n delete mode 100644 t/t4018/matlab-octave-section-1\n delete mode 100644 t/t4018/matlab-octave-section-2\n delete mode 100644 t/t4018/matlab-section\n create mode 100755 t/t4018/matlab.sh\n delete mode 100644 t/t4018/perl-skip-end-of-heredoc\n delete mode 100644 t/t4018/perl-skip-forward-decl\n delete mode 100644 t/t4018/perl-skip-sub-in-pod\n delete mode 100644 t/t4018/perl-sub-definition\n delete mode 100644 t/t4018/perl-sub-definition-kr-brace\n create mode 100755 t/t4018/perl.sh\n delete mode 100644 t/t4018/php-abstract-class\n delete mode 100644 t/t4018/php-abstract-method\n delete mode 100644 t/t4018/php-class\n delete mode 100644 t/t4018/php-final-class\n delete mode 100644 t/t4018/php-final-method\n delete mode 100644 t/t4018/php-function\n delete mode 100644 t/t4018/php-interface\n delete mode 100644 t/t4018/php-method\n delete mode 100644 t/t4018/php-trait\n create mode 100755 t/t4018/php.sh\n delete mode 100644 t/t4018/python-async-def\n delete mode 100644 t/t4018/python-class\n delete mode 100644 t/t4018/python-def\n delete mode 100644 t/t4018/python-indented-async-def\n delete mode 100644 t/t4018/python-indented-class\n delete mode 100644 t/t4018/python-indented-def\n create mode 100755 t/t4018/python.sh\n delete mode 100644 t/t4018/rust-fn\n delete mode 100644 t/t4018/rust-impl\n delete mode 100644 t/t4018/rust-macro-rules\n delete mode 100644 t/t4018/rust-struct\n delete mode 100644 t/t4018/rust-trait\n create mode 100755 t/t4018/rust.sh\n\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416891","messageId":"20210215005236.11313-5-avarab@gmail.com","threadId":"55144","inReplyTo":"87tuqebj6m.fsf@evledraar.gmail.com","subject":"[PATCH 04/20] userdiff tests: explicitly test \"default\" pattern","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T00:52:20Z","receivedAt":"2021-02-15T00:54:01Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Since 122aa6f9c0 (diff: introduce diff.<driver>.binary, 2008-10-05)\nthe internals of the userdiff.c code have understood a \"default\" name,\nwhich is invoked as userdiff_find_by_name(\"default\") and present in\nthe \"builtin_drivers\" struct. Let's test for this special case.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018-diff-funcname.sh | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex 9675bc17db..cefe329aea 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -49,6 +49,7 @@ diffpatterns=\"\n \truby\n \trust\n \ttex\n+\tdefault\n \tcustom1\n \tcustom2\n \tcustom3\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416892","messageId":"20210215005236.11313-6-avarab@gmail.com","threadId":"55144","inReplyTo":"87tuqebj6m.fsf@evledraar.gmail.com","subject":"[PATCH 05/20] userdiff tests: list builtin drivers via test-tool","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T00:52:21Z","receivedAt":"2021-02-15T00:54:40Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the userdiff test to list the builtin drivers via the\ntest-tool, using the new for_each_userdiff_driver() API function.\n\nThis gets rid of the need to modify this part of the test every time a\nnew pattern is added, see 2ff6c34612 (userdiff: support Bash,\n2020-10-22) and 09dad9256a (userdiff: support Markdown, 2020-05-02)\nfor two recent examples.\n\nI only need the \"list-builtin-drivers \"argument here, but let's add\n\"list-custom-drivers\" and \"list-drivers\" too, just because it's easy.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Makefile                 |  1 +\n t/helper/test-tool.c     |  1 +\n t/helper/test-tool.h     |  1 +\n t/helper/test-userdiff.c | 32 ++++++++++++++++++++++++++++++++\n t/t4018-diff-funcname.sh | 29 +++++------------------------\n 5 files changed, 40 insertions(+), 24 deletions(-)\n create mode 100644 t/helper/test-userdiff.c\n\ndiff --git a/Makefile b/Makefile\nindex 5a239cac20..710a0deaed 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -741,6 +741,7 @@ TEST_BUILTINS_OBJS += test-submodule-nested-repo-config.o\n TEST_BUILTINS_OBJS += test-subprocess.o\n TEST_BUILTINS_OBJS += test-trace2.o\n TEST_BUILTINS_OBJS += test-urlmatch-normalization.o\n+TEST_BUILTINS_OBJS += test-userdiff.o\n TEST_BUILTINS_OBJS += test-wildmatch.o\n TEST_BUILTINS_OBJS += test-windows-named-pipe.o\n TEST_BUILTINS_OBJS += test-write-cache.o\ndiff --git a/t/helper/test-tool.c b/t/helper/test-tool.c\nindex f97cd9f48a..dcb05ca6e5 100644\n--- a/t/helper/test-tool.c\n+++ b/t/helper/test-tool.c\n@@ -71,6 +71,7 @@ static struct test_cmd cmds[] = {\n \t{ \"submodule-nested-repo-config\", cmd__submodule_nested_repo_config },\n \t{ \"subprocess\", cmd__subprocess },\n \t{ \"trace2\", cmd__trace2 },\n+\t{ \"userdiff\", cmd__userdiff },\n \t{ \"urlmatch-normalization\", cmd__urlmatch_normalization },\n \t{ \"xml-encode\", cmd__xml_encode },\n \t{ \"wildmatch\", cmd__wildmatch },\ndiff --git a/t/helper/test-tool.h b/t/helper/test-tool.h\nindex 28072c0ad5..589f2e8ac6 100644\n--- a/t/helper/test-tool.h\n+++ b/t/helper/test-tool.h\n@@ -61,6 +61,7 @@ int cmd__submodule_config(int argc, const char **argv);\n int cmd__submodule_nested_repo_config(int argc, const char **argv);\n int cmd__subprocess(int argc, const char **argv);\n int cmd__trace2(int argc, const char **argv);\n+int cmd__userdiff(int argc, const char **argv);\n int cmd__urlmatch_normalization(int argc, const char **argv);\n int cmd__xml_encode(int argc, const char **argv);\n int cmd__wildmatch(int argc, const char **argv);\ndiff --git a/t/helper/test-userdiff.c b/t/helper/test-userdiff.c\nnew file mode 100644\nindex 0000000000..80de456287\n--- /dev/null\n+++ b/t/helper/test-userdiff.c\n@@ -0,0 +1,32 @@\n+#include \"test-tool.h\"\n+#include \"cache.h\"\n+#include \"userdiff.h\"\n+\n+static int userdiff_list_builtin_drivers_cb(struct userdiff_driver *driver,\n+\t\t\t\t\t    enum userdiff_driver_type type,\n+\t\t\t\t\t    void *priv)\n+{\n+\tputs(driver->name);\n+\treturn 0;\n+}\n+\n+static int list_what(enum userdiff_driver_type type)\n+{\n+\treturn for_each_userdiff_driver(userdiff_list_builtin_drivers_cb, type,\n+\t\t\t\t\tNULL);\n+}\n+\n+int cmd__userdiff(int argc, const char **argv)\n+{\n+\tif (argc != 2)\n+\t\treturn 1;\n+\n+\tif (!strcmp(argv[1], \"list-drivers\"))\n+\t\treturn list_what(USERDIFF_DRIVER_TYPE_UNSPECIFIED);\n+\telse if (!strcmp(argv[1], \"list-builtin-drivers\"))\n+\t\treturn list_what(USERDIFF_DRIVER_TYPE_BUILTIN);\n+\telse if (!strcmp(argv[1], \"list-custom-drivers\"))\n+\t\treturn list_what(USERDIFF_DRIVER_TYPE_CUSTOM);\n+\telse\n+\t\treturn error(\"unknown argument %s\", argv[1]);\n+}\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex cefe329aea..11ac648451 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -8,6 +8,10 @@ test_description='Test custom diff function name patterns'\n . ./test-lib.sh\n \n test_expect_success 'setup' '\n+\ttest-tool userdiff list-builtin-drivers >builtin-drivers &&\n+\ttest_file_not_empty builtin-drivers &&\n+\tbuiltin_drivers=$(cat builtin-drivers) &&\n+\n \t# a non-trivial custom pattern\n \tgit config diff.custom1.funcname \"!static\n !String\n@@ -26,30 +30,7 @@ test_expect_success 'setup' '\n '\n \n diffpatterns=\"\n-\tada\n-\tbash\n-\tbibtex\n-\tcpp\n-\tcsharp\n-\tcss\n-\tdts\n-\telixir\n-\tfortran\n-\tfountain\n-\tgolang\n-\thtml\n-\tjava\n-\tmarkdown\n-\tmatlab\n-\tobjc\n-\tpascal\n-\tperl\n-\tphp\n-\tpython\n-\truby\n-\trust\n-\ttex\n-\tdefault\n+\t$builtin_drivers\n \tcustom1\n \tcustom2\n \tcustom3\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416893","messageId":"20210215005236.11313-7-avarab@gmail.com","threadId":"55144","inReplyTo":"87tuqebj6m.fsf@evledraar.gmail.com","subject":"[PATCH 06/20] userdiff: remove support for \"broken\" tests","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T00:52:22Z","receivedAt":"2021-02-15T00:54:40Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"There have been no \"broken\" tests since 75c3b6b2e8 (userdiff: improve\nFortran xfuncname regex, 2020-08-12). Let's remove the test support\nfor them, this is in preparation for a more general refactoring of the\ntests.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018-diff-funcname.sh | 8 +-------\n t/t4018/README           | 3 ---\n 2 files changed, 1 insertion(+), 10 deletions(-)\n\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex 11ac648451..5fb5b0a651 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -83,13 +83,7 @@ test_expect_success 'setup hunk header tests' '\n # check each individual file\n for i in $(git ls-files)\n do\n-\tif grep broken \"$i\" >/dev/null 2>&1\n-\tthen\n-\t\tresult=failure\n-\telse\n-\t\tresult=success\n-\tfi\n-\ttest_expect_$result \"hunk header: $i\" \"\n+\ttest_expect_success \"hunk header: $i\" \"\n \t\tgit diff -U1 $i >actual &&\n \t\tgrep '@@ .* @@.*RIGHT' actual\n \t\"\ndiff --git a/t/t4018/README b/t/t4018/README\nindex 283e01cca1..2d25b2b4fc 100644\n--- a/t/t4018/README\n+++ b/t/t4018/README\n@@ -7,9 +7,6 @@ at least two lines from the line that must appear in the hunk header.\n The text that must appear in the hunk header must contain the word\n \"right\", but in all upper-case, like in the title above.\n \n-To mark a test case that highlights a malfunction, insert the word\n-BROKEN in all lower-case somewhere in the file.\n-\n This text is a bit twisted and out of order, but it is itself a\n test case for the default hunk header pattern. Know what you are doing\n if you change it.\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416894","messageId":"20210215005236.11313-10-avarab@gmail.com","threadId":"55144","inReplyTo":"87tuqebj6m.fsf@evledraar.gmail.com","subject":"[PATCH 09/20] blame tests: don't rely on t/t4018/ directory","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T00:52:25Z","receivedAt":"2021-02-15T00:54:40Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Refactor a test added in 9466e3809d (blame: enable funcname blaming\nwith userdiff driver, 2020-11-01) so that the blame tests don't rely\non stealing the contents of \"t/t4018/fortran-external-function\".\n\nI'm about to refactor that directory, just moving the relevant test\nfile here inline is the easiest solution, and I think also the most\nreadable.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/annotate-tests.sh | 16 +++++++++++++---\n 1 file changed, 13 insertions(+), 3 deletions(-)\n\ndiff --git a/t/annotate-tests.sh b/t/annotate-tests.sh\nindex 29ce89090d..04a2c58594 100644\n--- a/t/annotate-tests.sh\n+++ b/t/annotate-tests.sh\n@@ -482,12 +482,22 @@ test_expect_success 'blame -L ^:RE (absolute: end-of-file)' '\n test_expect_success 'setup -L :funcname with userdiff driver' '\n \techo \"fortran-* diff=fortran\" >.gitattributes &&\n \tfortran_file=fortran-external-function &&\n-\torig_file=\"$TEST_DIRECTORY/t4018/$fortran_file\" &&\n-\tcp \"$orig_file\" . &&\n+\tcat >$fortran_file <<-\\EOF &&\n+\tfunction RIGHT(a, b) result(c)\n+\n+\tinteger, intent(in) :: ChangeMe\n+\tinteger, intent(in) :: b\n+\tinteger, intent(out) :: c\n+\n+\tc = a+b\n+\n+\tend function RIGHT\n+\tEOF\n \tgit add \"$fortran_file\" &&\n \tGIT_AUTHOR_NAME=\"A\" GIT_AUTHOR_EMAIL=\"A@test.git\" \\\n \tgit commit -m \"add fortran file\" &&\n-\tsed -e \"s/ChangeMe/IWasChanged/\" <\"$orig_file\" >\"$fortran_file\" &&\n+\tsed -e \"s/ChangeMe/IWasChanged/\" <\"$fortran_file\" >\"$fortran_file\".tmp &&\n+\tmv \"$fortran_file\".tmp \"$fortran_file\" &&\n \tgit add \"$fortran_file\" &&\n \tGIT_AUTHOR_NAME=\"B\" GIT_AUTHOR_EMAIL=\"B@test.git\" \\\n \tgit commit -m \"change fortran file\"\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416895","messageId":"20210215005236.11313-11-avarab@gmail.com","threadId":"55144","inReplyTo":"87tuqebj6m.fsf@evledraar.gmail.com","subject":"[PATCH 10/20] userdiff tests: move custom patterns into one test file","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T00:52:26Z","receivedAt":"2021-02-15T00:54:40Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"In a preceding commit the test infrastructure got rewritten so\n\"t/t4018/\" are now normal test files which can do things like set\nconfig, so let's make it responsible for setting up and tearing down\nthe config for its tests.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018-diff-funcname.sh | 16 +-------\n t/t4018/custom.sh        | 84 ++++++++++++++++++++++++++++++++++++++++\n t/t4018/custom1.sh       | 27 -------------\n t/t4018/custom2.sh       | 18 ---------\n t/t4018/custom3.sh       | 27 -------------\n 5 files changed, 85 insertions(+), 87 deletions(-)\n create mode 100755 t/t4018/custom.sh\n delete mode 100755 t/t4018/custom1.sh\n delete mode 100755 t/t4018/custom2.sh\n delete mode 100755 t/t4018/custom3.sh\n\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex 3ff34c13d7..3e4c07e42b 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -12,18 +12,6 @@ test_expect_success 'setup' '\n \ttest_file_not_empty builtin-drivers &&\n \tbuiltin_drivers=$(cat builtin-drivers) &&\n \n-\t# a non-trivial custom pattern\n-\tgit config diff.custom1.funcname \"!static\n-!String\n-[^ \t].*s.*\" &&\n-\n-\t# a custom pattern which matches to end of line\n-\tgit config diff.custom2.funcname \"......Beer\\$\" &&\n-\n-\t# alternation in pattern\n-\tgit config diff.custom3.funcname \"Beer$\" &&\n-\tgit config diff.custom3.xfuncname \"^[ \t]*((public|static).*)$\" &&\n-\n \t# for regexp compilation tests\n \techo A >A.java &&\n \techo B >B.java\n@@ -31,9 +19,7 @@ test_expect_success 'setup' '\n \n diffpatterns=\"\n \t$builtin_drivers\n-\tcustom1\n-\tcustom2\n-\tcustom3\n+\tcustom\n \"\n \n for p in $diffpatterns\ndiff --git a/t/t4018/custom.sh b/t/t4018/custom.sh\nnew file mode 100755\nindex 0000000000..69f1f7339f\n--- /dev/null\n+++ b/t/t4018/custom.sh\n@@ -0,0 +1,84 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_expect_success 'custom: setup non-trivial custom' '\n+\tgit config diff.custom.funcname \"!static\n+!String\n+[^ \t].*s.*\"\n+'\n+\n+test_diff_funcname 'custom: non-trivial custom pattern' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+int special, RIGHT;\n+EOF_HUNK\n+public class Beer\n+{\n+\tint special, RIGHT;\n+\tpublic static void main(String args[])\n+\t{\n+\t\tString s=\" \";\n+\t\tfor(int x = 99; x > 0; x--)\n+\t\t{\n+\t\t\tSystem.out.print(x + \" bottles of beer on the wall \"\n+\t\t\t\t+ x + \" bottles of beer\\n\" // ChangeMe\n+\t\t\t\t+ \"Take one down, pass it around, \" + (x - 1)\n+\t\t\t\t+ \" bottles of beer on the wall.\\n\");\n+\t\t}\n+\t\tSystem.out.print(\"Go to the store, buy some more,\\n\"\n+\t\t\t+ \"99 bottles of beer on the wall.\\n\");\n+\t}\n+}\n+EOF_TEST\n+\n+test_expect_success 'custom: setup match to end of line' '\n+\tgit config diff.custom.funcname \"......Beer\\$\"\n+'\n+\n+test_diff_funcname 'custom: match to end of line' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT_Beer\n+EOF_HUNK\n+public class RIGHT_Beer\n+{\n+\tint special;\n+\tpublic static void main(String args[])\n+\t{\n+\t\tSystem.out.print(\"ChangeMe\");\n+\t}\n+}\n+EOF_TEST\n+\n+test_expect_success 'custom: setup alternation in pattern' '\n+\tgit config diff.custom.funcname \"Beer$\" &&\n+\tgit config diff.custom.xfuncname \"^[ \t]*((public|static).*)$\"\n+'\n+\n+test_diff_funcname 'custom: alternation in pattern' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+public static void main(String RIGHT[])\n+EOF_HUNK\n+public class Beer\n+{\n+\tint special;\n+\tpublic static void main(String RIGHT[])\n+\t{\n+\t\tString s=\" \";\n+\t\tfor(int x = 99; x > 0; x--)\n+\t\t{\n+\t\t\tSystem.out.print(x + \" bottles of beer on the wall \"\n+\t\t\t\t+ x + \" bottles of beer\\n\" // ChangeMe\n+\t\t\t\t+ \"Take one down, pass it around, \" + (x - 1)\n+\t\t\t\t+ \" bottles of beer on the wall.\\n\");\n+\t\t}\n+\t\tSystem.out.print(\"Go to the store, buy some more,\\n\"\n+\t\t\t+ \"99 bottles of beer on the wall.\\n\");\n+\t}\n+}\n+EOF_TEST\n+\n+test_expect_success 'custom: teardown' '\n+\ttest_unconfig diff.custom.funcname &&\n+\ttest_unconfig diff.custom.xfuncname\n+'\ndiff --git a/t/t4018/custom1.sh b/t/t4018/custom1.sh\ndeleted file mode 100755\nindex f8bbccadb4..0000000000\n--- a/t/t4018/custom1.sh\n+++ /dev/null\n@@ -1,27 +0,0 @@\n-#!/bin/sh\n-#\n-# See ../t4018-diff-funcname.sh's test_diff_funcname()\n-#\n-\n-test_diff_funcname 'custom1: pattern' \\\n-\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n-int special, RIGHT;\n-EOF_HUNK\n-public class Beer\n-{\n-\tint special, RIGHT;\n-\tpublic static void main(String args[])\n-\t{\n-\t\tString s=\" \";\n-\t\tfor(int x = 99; x > 0; x--)\n-\t\t{\n-\t\t\tSystem.out.print(x + \" bottles of beer on the wall \"\n-\t\t\t\t+ x + \" bottles of beer\\n\" // ChangeMe\n-\t\t\t\t+ \"Take one down, pass it around, \" + (x - 1)\n-\t\t\t\t+ \" bottles of beer on the wall.\\n\");\n-\t\t}\n-\t\tSystem.out.print(\"Go to the store, buy some more,\\n\"\n-\t\t\t+ \"99 bottles of beer on the wall.\\n\");\n-\t}\n-}\n-EOF_TEST\ndiff --git a/t/t4018/custom2.sh b/t/t4018/custom2.sh\ndeleted file mode 100755\nindex c68421f788..0000000000\n--- a/t/t4018/custom2.sh\n+++ /dev/null\n@@ -1,18 +0,0 @@\n-#!/bin/sh\n-#\n-# See ../t4018-diff-funcname.sh's test_diff_funcname()\n-#\n-\n-test_diff_funcname 'custom2: match to end of line' \\\n-\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n-RIGHT_Beer\n-EOF_HUNK\n-public class RIGHT_Beer\n-{\n-\tint special;\n-\tpublic static void main(String args[])\n-\t{\n-\t\tSystem.out.print(\"ChangeMe\");\n-\t}\n-}\n-EOF_TEST\ndiff --git a/t/t4018/custom3.sh b/t/t4018/custom3.sh\ndeleted file mode 100755\nindex 07c5c134ff..0000000000\n--- a/t/t4018/custom3.sh\n+++ /dev/null\n@@ -1,27 +0,0 @@\n-#!/bin/sh\n-#\n-# See ../t4018-diff-funcname.sh's test_diff_funcname()\n-#\n-\n-test_diff_funcname 'custom3: alternation in pattern' \\\n-\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n-public static void main(String RIGHT[])\n-EOF_HUNK\n-public class Beer\n-{\n-\tint special;\n-\tpublic static void main(String RIGHT[])\n-\t{\n-\t\tString s=\" \";\n-\t\tfor(int x = 99; x > 0; x--)\n-\t\t{\n-\t\t\tSystem.out.print(x + \" bottles of beer on the wall \"\n-\t\t\t\t+ x + \" bottles of beer\\n\" // ChangeMe\n-\t\t\t\t+ \"Take one down, pass it around, \" + (x - 1)\n-\t\t\t\t+ \" bottles of beer on the wall.\\n\");\n-\t\t}\n-\t\tSystem.out.print(\"Go to the store, buy some more,\\n\"\n-\t\t\t+ \"99 bottles of beer on the wall.\\n\");\n-\t}\n-}\n-EOF_TEST\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416897","messageId":"20210215005236.11313-8-avarab@gmail.com","threadId":"55144","inReplyTo":"87tuqebj6m.fsf@evledraar.gmail.com","subject":"[PATCH 07/20] userdiff tests: match full hunk headers","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T00:52:23Z","receivedAt":"2021-02-15T00:54:40Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a regression in the test framework for userdiff added in\nbfa7d01413 (t4018: an infrastructure to test hunk headers,\n2014-03-21).\n\nThe testing infrastructure added in that change went overboard with\nsimplifying the tests, to the point where we lost test coverage.\n\nBefore that we'd been able to test the full context line, or ever\nsince the feature was originally added in f258475a6e (Per-path\nattribute based hunk header selection., 2007-07-06).\n\nAfter bfa7d01413 all we cared about was whether \"RIGHT\" appeared on\nthe line. We thus lost the information about whether or not \"RIGHT\"\nwas extracted from the line for the hunk header, or the line appeared\nin full (or other subset of the line).\n\nLet's bring back coverage for that by adding corresponding *.ctx\nfiles, this has the added advantage that we're doing a \"test_cmp\", so\nwhen we have failures it's just a non-zero exit code from \"grep\",\nwe'll actually have something meaningful in the \"-v\" output.\n\nAs we'll see in a follow-up commit this is an intermediate step\ntowards even better test coverage. I'm structuring these tests in such\na way as to benefit from the diff.colorMove detection.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018-diff-funcname.sh                      |  7 +++---\n t/t4018/README                                | 22 +++++++++----------\n t/t4018/README.ctx                            |  1 +\n t/t4018/bash-arithmetic-function.ctx          |  1 +\n t/t4018/bash-bashism-style-compact.ctx        |  1 +\n t/t4018/bash-bashism-style-function.ctx       |  1 +\n t/t4018/bash-bashism-style-whitespace.ctx     |  1 +\n t/t4018/bash-conditional-function.ctx         |  1 +\n t/t4018/bash-missing-parentheses.ctx          |  1 +\n t/t4018/bash-mixed-style-compact.ctx          |  1 +\n t/t4018/bash-mixed-style-function.ctx         |  1 +\n t/t4018/bash-nested-functions.ctx             |  1 +\n t/t4018/bash-other-characters.ctx             |  1 +\n t/t4018/bash-posix-style-compact.ctx          |  1 +\n t/t4018/bash-posix-style-function.ctx         |  1 +\n t/t4018/bash-posix-style-whitespace.ctx       |  1 +\n t/t4018/bash-subshell-function.ctx            |  1 +\n t/t4018/bash-trailing-comment.ctx             |  1 +\n t/t4018/cpp-c++-function.ctx                  |  1 +\n t/t4018/cpp-class-constructor-mem-init.ctx    |  1 +\n t/t4018/cpp-class-constructor.ctx             |  1 +\n t/t4018/cpp-class-definition-derived.ctx      |  1 +\n t/t4018/cpp-class-definition.ctx              |  1 +\n t/t4018/cpp-class-destructor.ctx              |  1 +\n .../cpp-function-returning-global-type.ctx    |  1 +\n t/t4018/cpp-function-returning-nested.ctx     |  1 +\n t/t4018/cpp-function-returning-pointer.ctx    |  1 +\n t/t4018/cpp-function-returning-reference.ctx  |  1 +\n t/t4018/cpp-gnu-style-function.ctx            |  1 +\n t/t4018/cpp-namespace-definition.ctx          |  1 +\n t/t4018/cpp-operator-definition.ctx           |  1 +\n t/t4018/cpp-skip-access-specifiers.ctx        |  1 +\n t/t4018/cpp-skip-comment-block.ctx            |  1 +\n t/t4018/cpp-skip-labels.ctx                   |  1 +\n t/t4018/cpp-struct-definition.ctx             |  1 +\n t/t4018/cpp-struct-single-line.ctx            |  1 +\n t/t4018/cpp-template-function-definition.ctx  |  1 +\n t/t4018/cpp-union-definition.ctx              |  1 +\n t/t4018/cpp-void-c-function.ctx               |  1 +\n t/t4018/css-attribute-value-selector.ctx      |  1 +\n t/t4018/css-block-level-@-statements.ctx      |  1 +\n t/t4018/css-brace-in-col-1.ctx                |  1 +\n t/t4018/css-class-selector.ctx                |  1 +\n t/t4018/css-colon-eol.ctx                     |  1 +\n t/t4018/css-colon-selector.ctx                |  1 +\n t/t4018/css-common.ctx                        |  1 +\n t/t4018/css-id-selector.ctx                   |  1 +\n t/t4018/css-long-selector-list.ctx            |  1 +\n t/t4018/css-prop-sans-indent.ctx              |  1 +\n t/t4018/css-root-selector.ctx                 |  1 +\n t/t4018/css-short-selector-list.ctx           |  1 +\n t/t4018/css-trailing-space.ctx                |  1 +\n t/t4018/custom1-pattern.ctx                   |  1 +\n t/t4018/custom2-match-to-end-of-line.ctx      |  1 +\n t/t4018/custom3-alternation-in-pattern.ctx    |  1 +\n t/t4018/dts-labels.ctx                        |  1 +\n t/t4018/dts-node-unitless.ctx                 |  1 +\n t/t4018/dts-nodes-boolean-prop.ctx            |  1 +\n t/t4018/dts-nodes-comment1.ctx                |  1 +\n t/t4018/dts-nodes-comment2.ctx                |  1 +\n t/t4018/dts-nodes-multiline-prop.ctx          |  1 +\n t/t4018/dts-nodes.ctx                         |  1 +\n t/t4018/dts-reference.ctx                     |  1 +\n t/t4018/dts-root-comment.ctx                  |  1 +\n t/t4018/dts-root.ctx                          |  1 +\n t/t4018/elixir-do-not-pick-end.ctx            |  1 +\n t/t4018/elixir-ex-unit-test.ctx               |  1 +\n t/t4018/elixir-function.ctx                   |  1 +\n t/t4018/elixir-macro.ctx                      |  1 +\n t/t4018/elixir-module-func.ctx                |  1 +\n t/t4018/elixir-module.ctx                     |  1 +\n t/t4018/elixir-nested-module.ctx              |  1 +\n t/t4018/elixir-private-function.ctx           |  1 +\n t/t4018/elixir-protocol-implementation.ctx    |  1 +\n t/t4018/elixir-protocol.ctx                   |  1 +\n t/t4018/fortran-block-data.ctx                |  1 +\n t/t4018/fortran-comment-keyword.ctx           |  1 +\n t/t4018/fortran-comment-legacy-star.ctx       |  1 +\n t/t4018/fortran-comment-legacy.ctx            |  1 +\n t/t4018/fortran-comment.ctx                   |  1 +\n t/t4018/fortran-external-function.ctx         |  1 +\n t/t4018/fortran-external-subroutine.ctx       |  1 +\n t/t4018/fortran-module-procedure.ctx          |  1 +\n t/t4018/fortran-module.ctx                    |  1 +\n t/t4018/fortran-program.ctx                   |  1 +\n t/t4018/fountain-scene.ctx                    |  1 +\n t/t4018/golang-complex-function.ctx           |  1 +\n t/t4018/golang-func.ctx                       |  1 +\n t/t4018/golang-interface.ctx                  |  1 +\n t/t4018/golang-long-func.ctx                  |  1 +\n t/t4018/golang-struct.ctx                     |  1 +\n t/t4018/java-class-member-function.ctx        |  1 +\n t/t4018/markdown-heading-indented.ctx         |  1 +\n t/t4018/markdown-heading-non-headings.ctx     |  1 +\n t/t4018/matlab-class-definition.ctx           |  1 +\n t/t4018/matlab-function.ctx                   |  1 +\n t/t4018/matlab-octave-section-1.ctx           |  1 +\n t/t4018/matlab-octave-section-2.ctx           |  1 +\n t/t4018/matlab-section.ctx                    |  1 +\n t/t4018/perl-skip-end-of-heredoc.ctx          |  1 +\n t/t4018/perl-skip-forward-decl.ctx            |  1 +\n t/t4018/perl-skip-sub-in-pod.ctx              |  1 +\n t/t4018/perl-sub-definition-kr-brace.ctx      |  1 +\n t/t4018/perl-sub-definition.ctx               |  1 +\n t/t4018/php-abstract-class.ctx                |  1 +\n t/t4018/php-abstract-method.ctx               |  1 +\n t/t4018/php-class.ctx                         |  1 +\n t/t4018/php-final-class.ctx                   |  1 +\n t/t4018/php-final-method.ctx                  |  1 +\n t/t4018/php-function.ctx                      |  1 +\n t/t4018/php-interface.ctx                     |  1 +\n t/t4018/php-method.ctx                        |  1 +\n t/t4018/php-trait.ctx                         |  1 +\n t/t4018/python-async-def.ctx                  |  1 +\n t/t4018/python-class.ctx                      |  1 +\n t/t4018/python-def.ctx                        |  1 +\n t/t4018/python-indented-async-def.ctx         |  1 +\n t/t4018/python-indented-class.ctx             |  1 +\n t/t4018/python-indented-def.ctx               |  1 +\n t/t4018/rust-fn.ctx                           |  1 +\n t/t4018/rust-impl.ctx                         |  1 +\n t/t4018/rust-macro-rules.ctx                  |  1 +\n t/t4018/rust-struct.ctx                       |  1 +\n t/t4018/rust-trait.ctx                        |  1 +\n 124 files changed, 137 insertions(+), 14 deletions(-)\n create mode 100644 t/t4018/README.ctx\n create mode 100644 t/t4018/bash-arithmetic-function.ctx\n create mode 100644 t/t4018/bash-bashism-style-compact.ctx\n create mode 100644 t/t4018/bash-bashism-style-function.ctx\n create mode 100644 t/t4018/bash-bashism-style-whitespace.ctx\n create mode 100644 t/t4018/bash-conditional-function.ctx\n create mode 100644 t/t4018/bash-missing-parentheses.ctx\n create mode 100644 t/t4018/bash-mixed-style-compact.ctx\n create mode 100644 t/t4018/bash-mixed-style-function.ctx\n create mode 100644 t/t4018/bash-nested-functions.ctx\n create mode 100644 t/t4018/bash-other-characters.ctx\n create mode 100644 t/t4018/bash-posix-style-compact.ctx\n create mode 100644 t/t4018/bash-posix-style-function.ctx\n create mode 100644 t/t4018/bash-posix-style-whitespace.ctx\n create mode 100644 t/t4018/bash-subshell-function.ctx\n create mode 100644 t/t4018/bash-trailing-comment.ctx\n create mode 100644 t/t4018/cpp-c++-function.ctx\n create mode 100644 t/t4018/cpp-class-constructor-mem-init.ctx\n create mode 100644 t/t4018/cpp-class-constructor.ctx\n create mode 100644 t/t4018/cpp-class-definition-derived.ctx\n create mode 100644 t/t4018/cpp-class-definition.ctx\n create mode 100644 t/t4018/cpp-class-destructor.ctx\n create mode 100644 t/t4018/cpp-function-returning-global-type.ctx\n create mode 100644 t/t4018/cpp-function-returning-nested.ctx\n create mode 100644 t/t4018/cpp-function-returning-pointer.ctx\n create mode 100644 t/t4018/cpp-function-returning-reference.ctx\n create mode 100644 t/t4018/cpp-gnu-style-function.ctx\n create mode 100644 t/t4018/cpp-namespace-definition.ctx\n create mode 100644 t/t4018/cpp-operator-definition.ctx\n create mode 100644 t/t4018/cpp-skip-access-specifiers.ctx\n create mode 100644 t/t4018/cpp-skip-comment-block.ctx\n create mode 100644 t/t4018/cpp-skip-labels.ctx\n create mode 100644 t/t4018/cpp-struct-definition.ctx\n create mode 100644 t/t4018/cpp-struct-single-line.ctx\n create mode 100644 t/t4018/cpp-template-function-definition.ctx\n create mode 100644 t/t4018/cpp-union-definition.ctx\n create mode 100644 t/t4018/cpp-void-c-function.ctx\n create mode 100644 t/t4018/css-attribute-value-selector.ctx\n create mode 100644 t/t4018/css-block-level-@-statements.ctx\n create mode 100644 t/t4018/css-brace-in-col-1.ctx\n create mode 100644 t/t4018/css-class-selector.ctx\n create mode 100644 t/t4018/css-colon-eol.ctx\n create mode 100644 t/t4018/css-colon-selector.ctx\n create mode 100644 t/t4018/css-common.ctx\n create mode 100644 t/t4018/css-id-selector.ctx\n create mode 100644 t/t4018/css-long-selector-list.ctx\n create mode 100644 t/t4018/css-prop-sans-indent.ctx\n create mode 100644 t/t4018/css-root-selector.ctx\n create mode 100644 t/t4018/css-short-selector-list.ctx\n create mode 100644 t/t4018/css-trailing-space.ctx\n create mode 100644 t/t4018/custom1-pattern.ctx\n create mode 100644 t/t4018/custom2-match-to-end-of-line.ctx\n create mode 100644 t/t4018/custom3-alternation-in-pattern.ctx\n create mode 100644 t/t4018/dts-labels.ctx\n create mode 100644 t/t4018/dts-node-unitless.ctx\n create mode 100644 t/t4018/dts-nodes-boolean-prop.ctx\n create mode 100644 t/t4018/dts-nodes-comment1.ctx\n create mode 100644 t/t4018/dts-nodes-comment2.ctx\n create mode 100644 t/t4018/dts-nodes-multiline-prop.ctx\n create mode 100644 t/t4018/dts-nodes.ctx\n create mode 100644 t/t4018/dts-reference.ctx\n create mode 100644 t/t4018/dts-root-comment.ctx\n create mode 100644 t/t4018/dts-root.ctx\n create mode 100644 t/t4018/elixir-do-not-pick-end.ctx\n create mode 100644 t/t4018/elixir-ex-unit-test.ctx\n create mode 100644 t/t4018/elixir-function.ctx\n create mode 100644 t/t4018/elixir-macro.ctx\n create mode 100644 t/t4018/elixir-module-func.ctx\n create mode 100644 t/t4018/elixir-module.ctx\n create mode 100644 t/t4018/elixir-nested-module.ctx\n create mode 100644 t/t4018/elixir-private-function.ctx\n create mode 100644 t/t4018/elixir-protocol-implementation.ctx\n create mode 100644 t/t4018/elixir-protocol.ctx\n create mode 100644 t/t4018/fortran-block-data.ctx\n create mode 100644 t/t4018/fortran-comment-keyword.ctx\n create mode 100644 t/t4018/fortran-comment-legacy-star.ctx\n create mode 100644 t/t4018/fortran-comment-legacy.ctx\n create mode 100644 t/t4018/fortran-comment.ctx\n create mode 100644 t/t4018/fortran-external-function.ctx\n create mode 100644 t/t4018/fortran-external-subroutine.ctx\n create mode 100644 t/t4018/fortran-module-procedure.ctx\n create mode 100644 t/t4018/fortran-module.ctx\n create mode 100644 t/t4018/fortran-program.ctx\n create mode 100644 t/t4018/fountain-scene.ctx\n create mode 100644 t/t4018/golang-complex-function.ctx\n create mode 100644 t/t4018/golang-func.ctx\n create mode 100644 t/t4018/golang-interface.ctx\n create mode 100644 t/t4018/golang-long-func.ctx\n create mode 100644 t/t4018/golang-struct.ctx\n create mode 100644 t/t4018/java-class-member-function.ctx\n create mode 100644 t/t4018/markdown-heading-indented.ctx\n create mode 100644 t/t4018/markdown-heading-non-headings.ctx\n create mode 100644 t/t4018/matlab-class-definition.ctx\n create mode 100644 t/t4018/matlab-function.ctx\n create mode 100644 t/t4018/matlab-octave-section-1.ctx\n create mode 100644 t/t4018/matlab-octave-section-2.ctx\n create mode 100644 t/t4018/matlab-section.ctx\n create mode 100644 t/t4018/perl-skip-end-of-heredoc.ctx\n create mode 100644 t/t4018/perl-skip-forward-decl.ctx\n create mode 100644 t/t4018/perl-skip-sub-in-pod.ctx\n create mode 100644 t/t4018/perl-sub-definition-kr-brace.ctx\n create mode 100644 t/t4018/perl-sub-definition.ctx\n create mode 100644 t/t4018/php-abstract-class.ctx\n create mode 100644 t/t4018/php-abstract-method.ctx\n create mode 100644 t/t4018/php-class.ctx\n create mode 100644 t/t4018/php-final-class.ctx\n create mode 100644 t/t4018/php-final-method.ctx\n create mode 100644 t/t4018/php-function.ctx\n create mode 100644 t/t4018/php-interface.ctx\n create mode 100644 t/t4018/php-method.ctx\n create mode 100644 t/t4018/php-trait.ctx\n create mode 100644 t/t4018/python-async-def.ctx\n create mode 100644 t/t4018/python-class.ctx\n create mode 100644 t/t4018/python-def.ctx\n create mode 100644 t/t4018/python-indented-async-def.ctx\n create mode 100644 t/t4018/python-indented-class.ctx\n create mode 100644 t/t4018/python-indented-def.ctx\n create mode 100644 t/t4018/rust-fn.ctx\n create mode 100644 t/t4018/rust-impl.ctx\n create mode 100644 t/t4018/rust-macro-rules.ctx\n create mode 100644 t/t4018/rust-struct.ctx\n create mode 100644 t/t4018/rust-trait.ctx\n\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex 5fb5b0a651..d32c38ad1a 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -81,11 +81,12 @@ test_expect_success 'setup hunk header tests' '\n '\n \n # check each individual file\n-for i in $(git ls-files)\n+for i in $(git ls-files -- ':!*.ctx')\n do\n \ttest_expect_success \"hunk header: $i\" \"\n-\t\tgit diff -U1 $i >actual &&\n-\t\tgrep '@@ .* @@.*RIGHT' actual\n+\t\tgit diff -U1 $i >diff &&\n+\t\tsed -n -e 's/^.*@@\\( \\|$\\)//p' <diff >ctx &&\n+\t\ttest_cmp $i.ctx ctx\n \t\"\n done\n \ndiff --git a/t/t4018/README b/t/t4018/README\nindex 2d25b2b4fc..2cccf8c950 100644\n--- a/t/t4018/README\n+++ b/t/t4018/README\n@@ -1,15 +1,15 @@\n-How to write RIGHT test cases\n-=============================\n+How to write test cases\n+=======================\n+\n+Create test cases called \"LANG-whatever\" in this directory, where\n+\"LANG\" is e.g. the userdiff driver name, where \"whatever\" is a brief\n+description of the test.\n \n Insert the word \"ChangeMe\" (exactly this form) at a distance of\n at least two lines from the line that must appear in the hunk header.\n \n-The text that must appear in the hunk header must contain the word\n-\"right\", but in all upper-case, like in the title above.\n-\n-This text is a bit twisted and out of order, but it is itself a\n-test case for the default hunk header pattern. Know what you are doing\n-if you change it.\n-\n-BTW, this tests that the head line goes to the hunk header, not the line\n-of equal signs.\n+The text that must appear in the hunk header must contains the word\n+\"RIGHT\" by convention. The \"LANG-whatever.ctx\" file contains what we\n+expect to appear in the hunk header. We munged the start of the line\n+to \"@@ [...] @@\" for ease of not having to hardcode the line numbers\n+and offsets.\ndiff --git a/t/t4018/README.ctx b/t/t4018/README.ctx\nnew file mode 100644\nindex 0000000000..cd79384b04\n--- /dev/null\n+++ b/t/t4018/README.ctx\n@@ -0,0 +1 @@\n+description of the test.\ndiff --git a/t/t4018/bash-arithmetic-function.ctx b/t/t4018/bash-arithmetic-function.ctx\nnew file mode 100644\nindex 0000000000..811eac7d2f\n--- /dev/null\n+++ b/t/t4018/bash-arithmetic-function.ctx\n@@ -0,0 +1 @@\n+RIGHT()\ndiff --git a/t/t4018/bash-bashism-style-compact.ctx b/t/t4018/bash-bashism-style-compact.ctx\nnew file mode 100644\nindex 0000000000..4f8eac48c6\n--- /dev/null\n+++ b/t/t4018/bash-bashism-style-compact.ctx\n@@ -0,0 +1 @@\n+function RIGHT {\ndiff --git a/t/t4018/bash-bashism-style-function.ctx b/t/t4018/bash-bashism-style-function.ctx\nnew file mode 100644\nindex 0000000000..4f8eac48c6\n--- /dev/null\n+++ b/t/t4018/bash-bashism-style-function.ctx\n@@ -0,0 +1 @@\n+function RIGHT {\ndiff --git a/t/t4018/bash-bashism-style-whitespace.ctx b/t/t4018/bash-bashism-style-whitespace.ctx\nnew file mode 100644\nindex 0000000000..35dbd0220e\n--- /dev/null\n+++ b/t/t4018/bash-bashism-style-whitespace.ctx\n@@ -0,0 +1 @@\n+function \tRIGHT \t( \t) \t{\ndiff --git a/t/t4018/bash-conditional-function.ctx b/t/t4018/bash-conditional-function.ctx\nnew file mode 100644\nindex 0000000000..811eac7d2f\n--- /dev/null\n+++ b/t/t4018/bash-conditional-function.ctx\n@@ -0,0 +1 @@\n+RIGHT()\ndiff --git a/t/t4018/bash-missing-parentheses.ctx b/t/t4018/bash-missing-parentheses.ctx\nnew file mode 100644\nindex 0000000000..4f8eac48c6\n--- /dev/null\n+++ b/t/t4018/bash-missing-parentheses.ctx\n@@ -0,0 +1 @@\n+function RIGHT {\ndiff --git a/t/t4018/bash-mixed-style-compact.ctx b/t/t4018/bash-mixed-style-compact.ctx\nnew file mode 100644\nindex 0000000000..bba11074eb\n--- /dev/null\n+++ b/t/t4018/bash-mixed-style-compact.ctx\n@@ -0,0 +1 @@\n+function RIGHT(){\ndiff --git a/t/t4018/bash-mixed-style-function.ctx b/t/t4018/bash-mixed-style-function.ctx\nnew file mode 100644\nindex 0000000000..922b87a4aa\n--- /dev/null\n+++ b/t/t4018/bash-mixed-style-function.ctx\n@@ -0,0 +1 @@\n+function RIGHT() {\ndiff --git a/t/t4018/bash-nested-functions.ctx b/t/t4018/bash-nested-functions.ctx\nnew file mode 100644\nindex 0000000000..811eac7d2f\n--- /dev/null\n+++ b/t/t4018/bash-nested-functions.ctx\n@@ -0,0 +1 @@\n+RIGHT()\ndiff --git a/t/t4018/bash-other-characters.ctx b/t/t4018/bash-other-characters.ctx\nnew file mode 100644\nindex 0000000000..6a55317fdf\n--- /dev/null\n+++ b/t/t4018/bash-other-characters.ctx\n@@ -0,0 +1 @@\n+_RIGHT_0n()\ndiff --git a/t/t4018/bash-posix-style-compact.ctx b/t/t4018/bash-posix-style-compact.ctx\nnew file mode 100644\nindex 0000000000..811eac7d2f\n--- /dev/null\n+++ b/t/t4018/bash-posix-style-compact.ctx\n@@ -0,0 +1 @@\n+RIGHT()\ndiff --git a/t/t4018/bash-posix-style-function.ctx b/t/t4018/bash-posix-style-function.ctx\nnew file mode 100644\nindex 0000000000..811eac7d2f\n--- /dev/null\n+++ b/t/t4018/bash-posix-style-function.ctx\n@@ -0,0 +1 @@\n+RIGHT()\ndiff --git a/t/t4018/bash-posix-style-whitespace.ctx b/t/t4018/bash-posix-style-whitespace.ctx\nnew file mode 100644\nindex 0000000000..28f8698e14\n--- /dev/null\n+++ b/t/t4018/bash-posix-style-whitespace.ctx\n@@ -0,0 +1 @@\n+RIGHT \t( \t)\ndiff --git a/t/t4018/bash-subshell-function.ctx b/t/t4018/bash-subshell-function.ctx\nnew file mode 100644\nindex 0000000000..811eac7d2f\n--- /dev/null\n+++ b/t/t4018/bash-subshell-function.ctx\n@@ -0,0 +1 @@\n+RIGHT()\ndiff --git a/t/t4018/bash-trailing-comment.ctx b/t/t4018/bash-trailing-comment.ctx\nnew file mode 100644\nindex 0000000000..811eac7d2f\n--- /dev/null\n+++ b/t/t4018/bash-trailing-comment.ctx\n@@ -0,0 +1 @@\n+RIGHT()\ndiff --git a/t/t4018/cpp-c++-function.ctx b/t/t4018/cpp-c++-function.ctx\nnew file mode 100644\nindex 0000000000..337b49dbb3\n--- /dev/null\n+++ b/t/t4018/cpp-c++-function.ctx\n@@ -0,0 +1 @@\n+Item RIGHT::DoSomething( Args with_spaces )\ndiff --git a/t/t4018/cpp-class-constructor-mem-init.ctx b/t/t4018/cpp-class-constructor-mem-init.ctx\nnew file mode 100644\nindex 0000000000..6664b8b3d8\n--- /dev/null\n+++ b/t/t4018/cpp-class-constructor-mem-init.ctx\n@@ -0,0 +1 @@\n+Item::Item(int RIGHT) :\ndiff --git a/t/t4018/cpp-class-constructor.ctx b/t/t4018/cpp-class-constructor.ctx\nnew file mode 100644\nindex 0000000000..2dcadfc0ba\n--- /dev/null\n+++ b/t/t4018/cpp-class-constructor.ctx\n@@ -0,0 +1 @@\n+Item::Item(int RIGHT)\ndiff --git a/t/t4018/cpp-class-definition-derived.ctx b/t/t4018/cpp-class-definition-derived.ctx\nnew file mode 100644\nindex 0000000000..146f0a7b7c\n--- /dev/null\n+++ b/t/t4018/cpp-class-definition-derived.ctx\n@@ -0,0 +1 @@\n+class RIGHT :\ndiff --git a/t/t4018/cpp-class-definition.ctx b/t/t4018/cpp-class-definition.ctx\nnew file mode 100644\nindex 0000000000..54bff816d6\n--- /dev/null\n+++ b/t/t4018/cpp-class-definition.ctx\n@@ -0,0 +1 @@\n+class RIGHT\ndiff --git a/t/t4018/cpp-class-destructor.ctx b/t/t4018/cpp-class-destructor.ctx\nnew file mode 100644\nindex 0000000000..5390c17cf6\n--- /dev/null\n+++ b/t/t4018/cpp-class-destructor.ctx\n@@ -0,0 +1 @@\n+RIGHT::~RIGHT()\ndiff --git a/t/t4018/cpp-function-returning-global-type.ctx b/t/t4018/cpp-function-returning-global-type.ctx\nnew file mode 100644\nindex 0000000000..4dcdde25f4\n--- /dev/null\n+++ b/t/t4018/cpp-function-returning-global-type.ctx\n@@ -0,0 +1 @@\n+::Item get::it::RIGHT()\ndiff --git a/t/t4018/cpp-function-returning-nested.ctx b/t/t4018/cpp-function-returning-nested.ctx\nnew file mode 100644\nindex 0000000000..6ef73c8368\n--- /dev/null\n+++ b/t/t4018/cpp-function-returning-nested.ctx\n@@ -0,0 +1 @@\n+get::Item get::it::RIGHT()\ndiff --git a/t/t4018/cpp-function-returning-pointer.ctx b/t/t4018/cpp-function-returning-pointer.ctx\nnew file mode 100644\nindex 0000000000..bb0acce5c7\n--- /dev/null\n+++ b/t/t4018/cpp-function-returning-pointer.ctx\n@@ -0,0 +1 @@\n+const char *get_it_RIGHT(char *ptr)\ndiff --git a/t/t4018/cpp-function-returning-reference.ctx b/t/t4018/cpp-function-returning-reference.ctx\nnew file mode 100644\nindex 0000000000..76afe381fd\n--- /dev/null\n+++ b/t/t4018/cpp-function-returning-reference.ctx\n@@ -0,0 +1 @@\n+string& get::it::RIGHT(char *ptr)\ndiff --git a/t/t4018/cpp-gnu-style-function.ctx b/t/t4018/cpp-gnu-style-function.ctx\nnew file mode 100644\nindex 0000000000..1858287812\n--- /dev/null\n+++ b/t/t4018/cpp-gnu-style-function.ctx\n@@ -0,0 +1 @@\n+RIGHT(int arg)\ndiff --git a/t/t4018/cpp-namespace-definition.ctx b/t/t4018/cpp-namespace-definition.ctx\nnew file mode 100644\nindex 0000000000..14c29c4638\n--- /dev/null\n+++ b/t/t4018/cpp-namespace-definition.ctx\n@@ -0,0 +1 @@\n+namespace RIGHT\ndiff --git a/t/t4018/cpp-operator-definition.ctx b/t/t4018/cpp-operator-definition.ctx\nnew file mode 100644\nindex 0000000000..5b56778961\n--- /dev/null\n+++ b/t/t4018/cpp-operator-definition.ctx\n@@ -0,0 +1 @@\n+Value operator+(Value LEFT, Value RIGHT)\ndiff --git a/t/t4018/cpp-skip-access-specifiers.ctx b/t/t4018/cpp-skip-access-specifiers.ctx\nnew file mode 100644\nindex 0000000000..075bcd883b\n--- /dev/null\n+++ b/t/t4018/cpp-skip-access-specifiers.ctx\n@@ -0,0 +1 @@\n+class RIGHT : public Baseclass\ndiff --git a/t/t4018/cpp-skip-comment-block.ctx b/t/t4018/cpp-skip-comment-block.ctx\nnew file mode 100644\nindex 0000000000..656c59893d\n--- /dev/null\n+++ b/t/t4018/cpp-skip-comment-block.ctx\n@@ -0,0 +1 @@\n+struct item RIGHT(int i)\ndiff --git a/t/t4018/cpp-skip-labels.ctx b/t/t4018/cpp-skip-labels.ctx\nnew file mode 100644\nindex 0000000000..6b0635f7f7\n--- /dev/null\n+++ b/t/t4018/cpp-skip-labels.ctx\n@@ -0,0 +1 @@\n+void RIGHT (void)\ndiff --git a/t/t4018/cpp-struct-definition.ctx b/t/t4018/cpp-struct-definition.ctx\nnew file mode 100644\nindex 0000000000..48ed893279\n--- /dev/null\n+++ b/t/t4018/cpp-struct-definition.ctx\n@@ -0,0 +1 @@\n+struct RIGHT {\ndiff --git a/t/t4018/cpp-struct-single-line.ctx b/t/t4018/cpp-struct-single-line.ctx\nnew file mode 100644\nindex 0000000000..e3bc9d5017\n--- /dev/null\n+++ b/t/t4018/cpp-struct-single-line.ctx\n@@ -0,0 +1 @@\n+struct RIGHT_iterator_tag {};\ndiff --git a/t/t4018/cpp-template-function-definition.ctx b/t/t4018/cpp-template-function-definition.ctx\nnew file mode 100644\nindex 0000000000..c9da39cf65\n--- /dev/null\n+++ b/t/t4018/cpp-template-function-definition.ctx\n@@ -0,0 +1 @@\n+template<class T> int RIGHT(T arg)\ndiff --git a/t/t4018/cpp-union-definition.ctx b/t/t4018/cpp-union-definition.ctx\nnew file mode 100644\nindex 0000000000..2fc7b54fb8\n--- /dev/null\n+++ b/t/t4018/cpp-union-definition.ctx\n@@ -0,0 +1 @@\n+union RIGHT {\ndiff --git a/t/t4018/cpp-void-c-function.ctx b/t/t4018/cpp-void-c-function.ctx\nnew file mode 100644\nindex 0000000000..6b0635f7f7\n--- /dev/null\n+++ b/t/t4018/cpp-void-c-function.ctx\n@@ -0,0 +1 @@\n+void RIGHT (void)\ndiff --git a/t/t4018/css-attribute-value-selector.ctx b/t/t4018/css-attribute-value-selector.ctx\nnew file mode 100644\nindex 0000000000..7f8956251c\n--- /dev/null\n+++ b/t/t4018/css-attribute-value-selector.ctx\n@@ -0,0 +1 @@\n+[class*=\"RIGHT\"] {\ndiff --git a/t/t4018/css-block-level-@-statements.ctx b/t/t4018/css-block-level-@-statements.ctx\nnew file mode 100644\nindex 0000000000..7f5e90468c\n--- /dev/null\n+++ b/t/t4018/css-block-level-@-statements.ctx\n@@ -0,0 +1 @@\n+@keyframes RIGHT {\ndiff --git a/t/t4018/css-brace-in-col-1.ctx b/t/t4018/css-brace-in-col-1.ctx\nnew file mode 100644\nindex 0000000000..91a9105c6a\n--- /dev/null\n+++ b/t/t4018/css-brace-in-col-1.ctx\n@@ -0,0 +1 @@\n+RIGHT label.control-label\ndiff --git a/t/t4018/css-class-selector.ctx b/t/t4018/css-class-selector.ctx\nnew file mode 100644\nindex 0000000000..ac7367d7f4\n--- /dev/null\n+++ b/t/t4018/css-class-selector.ctx\n@@ -0,0 +1 @@\n+.RIGHT {\ndiff --git a/t/t4018/css-colon-eol.ctx b/t/t4018/css-colon-eol.ctx\nnew file mode 100644\nindex 0000000000..b68493b9b0\n--- /dev/null\n+++ b/t/t4018/css-colon-eol.ctx\n@@ -0,0 +1 @@\n+RIGHT h1 {\ndiff --git a/t/t4018/css-colon-selector.ctx b/t/t4018/css-colon-selector.ctx\nnew file mode 100644\nindex 0000000000..00b1a5aefe\n--- /dev/null\n+++ b/t/t4018/css-colon-selector.ctx\n@@ -0,0 +1 @@\n+RIGHT a:hover {\ndiff --git a/t/t4018/css-common.ctx b/t/t4018/css-common.ctx\nnew file mode 100644\nindex 0000000000..43686b4081\n--- /dev/null\n+++ b/t/t4018/css-common.ctx\n@@ -0,0 +1 @@\n+RIGHT label.control-label {\ndiff --git a/t/t4018/css-id-selector.ctx b/t/t4018/css-id-selector.ctx\nnew file mode 100644\nindex 0000000000..ce19f6d8dc\n--- /dev/null\n+++ b/t/t4018/css-id-selector.ctx\n@@ -0,0 +1 @@\n+#RIGHT {\ndiff --git a/t/t4018/css-long-selector-list.ctx b/t/t4018/css-long-selector-list.ctx\nnew file mode 100644\nindex 0000000000..bc8d0fb62c\n--- /dev/null\n+++ b/t/t4018/css-long-selector-list.ctx\n@@ -0,0 +1 @@\n+div ul#RIGHT {\ndiff --git a/t/t4018/css-prop-sans-indent.ctx b/t/t4018/css-prop-sans-indent.ctx\nnew file mode 100644\nindex 0000000000..cc880b2f44\n--- /dev/null\n+++ b/t/t4018/css-prop-sans-indent.ctx\n@@ -0,0 +1 @@\n+RIGHT, label.control-label {\ndiff --git a/t/t4018/css-root-selector.ctx b/t/t4018/css-root-selector.ctx\nnew file mode 100644\nindex 0000000000..3010cded2a\n--- /dev/null\n+++ b/t/t4018/css-root-selector.ctx\n@@ -0,0 +1 @@\n+:RIGHT {\ndiff --git a/t/t4018/css-short-selector-list.ctx b/t/t4018/css-short-selector-list.ctx\nnew file mode 100644\nindex 0000000000..9e5d87d126\n--- /dev/null\n+++ b/t/t4018/css-short-selector-list.ctx\n@@ -0,0 +1 @@\n+label.control, div ul#RIGHT {\ndiff --git a/t/t4018/css-trailing-space.ctx b/t/t4018/css-trailing-space.ctx\nnew file mode 100644\nindex 0000000000..43686b4081\n--- /dev/null\n+++ b/t/t4018/css-trailing-space.ctx\n@@ -0,0 +1 @@\n+RIGHT label.control-label {\ndiff --git a/t/t4018/custom1-pattern.ctx b/t/t4018/custom1-pattern.ctx\nnew file mode 100644\nindex 0000000000..d1609cc9a6\n--- /dev/null\n+++ b/t/t4018/custom1-pattern.ctx\n@@ -0,0 +1 @@\n+int special, RIGHT;\ndiff --git a/t/t4018/custom2-match-to-end-of-line.ctx b/t/t4018/custom2-match-to-end-of-line.ctx\nnew file mode 100644\nindex 0000000000..8294c6e49b\n--- /dev/null\n+++ b/t/t4018/custom2-match-to-end-of-line.ctx\n@@ -0,0 +1 @@\n+RIGHT_Beer\ndiff --git a/t/t4018/custom3-alternation-in-pattern.ctx b/t/t4018/custom3-alternation-in-pattern.ctx\nnew file mode 100644\nindex 0000000000..2125474b68\n--- /dev/null\n+++ b/t/t4018/custom3-alternation-in-pattern.ctx\n@@ -0,0 +1 @@\n+public static void main(String RIGHT[])\ndiff --git a/t/t4018/dts-labels.ctx b/t/t4018/dts-labels.ctx\nnew file mode 100644\nindex 0000000000..48d9373cab\n--- /dev/null\n+++ b/t/t4018/dts-labels.ctx\n@@ -0,0 +1 @@\n+label2: RIGHT {\ndiff --git a/t/t4018/dts-node-unitless.ctx b/t/t4018/dts-node-unitless.ctx\nnew file mode 100644\nindex 0000000000..82c8683fa1\n--- /dev/null\n+++ b/t/t4018/dts-node-unitless.ctx\n@@ -0,0 +1 @@\n+RIGHT {\ndiff --git a/t/t4018/dts-nodes-boolean-prop.ctx b/t/t4018/dts-nodes-boolean-prop.ctx\nnew file mode 100644\nindex 0000000000..3a0232d55d\n--- /dev/null\n+++ b/t/t4018/dts-nodes-boolean-prop.ctx\n@@ -0,0 +1 @@\n+RIGHT@deadf00,4000 {\ndiff --git a/t/t4018/dts-nodes-comment1.ctx b/t/t4018/dts-nodes-comment1.ctx\nnew file mode 100644\nindex 0000000000..ec364600b1\n--- /dev/null\n+++ b/t/t4018/dts-nodes-comment1.ctx\n@@ -0,0 +1 @@\n+RIGHT@deadf00,4000 /* &a comment */ {\ndiff --git a/t/t4018/dts-nodes-comment2.ctx b/t/t4018/dts-nodes-comment2.ctx\nnew file mode 100644\nindex 0000000000..75f0d75258\n--- /dev/null\n+++ b/t/t4018/dts-nodes-comment2.ctx\n@@ -0,0 +1 @@\n+RIGHT@deadf00,4000 { /* a trailing comment */\ndiff --git a/t/t4018/dts-nodes-multiline-prop.ctx b/t/t4018/dts-nodes-multiline-prop.ctx\nnew file mode 100644\nindex 0000000000..3a0232d55d\n--- /dev/null\n+++ b/t/t4018/dts-nodes-multiline-prop.ctx\n@@ -0,0 +1 @@\n+RIGHT@deadf00,4000 {\ndiff --git a/t/t4018/dts-nodes.ctx b/t/t4018/dts-nodes.ctx\nnew file mode 100644\nindex 0000000000..3a0232d55d\n--- /dev/null\n+++ b/t/t4018/dts-nodes.ctx\n@@ -0,0 +1 @@\n+RIGHT@deadf00,4000 {\ndiff --git a/t/t4018/dts-reference.ctx b/t/t4018/dts-reference.ctx\nnew file mode 100644\nindex 0000000000..c1e13409ee\n--- /dev/null\n+++ b/t/t4018/dts-reference.ctx\n@@ -0,0 +1 @@\n+&RIGHT {\ndiff --git a/t/t4018/dts-root-comment.ctx b/t/t4018/dts-root-comment.ctx\nnew file mode 100644\nindex 0000000000..656053dd42\n--- /dev/null\n+++ b/t/t4018/dts-root-comment.ctx\n@@ -0,0 +1 @@\n+/ { RIGHT /* Technically just supposed to be a slash and brace */\ndiff --git a/t/t4018/dts-root.ctx b/t/t4018/dts-root.ctx\nnew file mode 100644\nindex 0000000000..656053dd42\n--- /dev/null\n+++ b/t/t4018/dts-root.ctx\n@@ -0,0 +1 @@\n+/ { RIGHT /* Technically just supposed to be a slash and brace */\ndiff --git a/t/t4018/elixir-do-not-pick-end.ctx b/t/t4018/elixir-do-not-pick-end.ctx\nnew file mode 100644\nindex 0000000000..8f28a7a689\n--- /dev/null\n+++ b/t/t4018/elixir-do-not-pick-end.ctx\n@@ -0,0 +1 @@\n+defmodule RIGHT do\ndiff --git a/t/t4018/elixir-ex-unit-test.ctx b/t/t4018/elixir-ex-unit-test.ctx\nnew file mode 100644\nindex 0000000000..a55e3de2cc\n--- /dev/null\n+++ b/t/t4018/elixir-ex-unit-test.ctx\n@@ -0,0 +1 @@\n+test \"RIGHT\" do\ndiff --git a/t/t4018/elixir-function.ctx b/t/t4018/elixir-function.ctx\nnew file mode 100644\nindex 0000000000..62aee9c8b1\n--- /dev/null\n+++ b/t/t4018/elixir-function.ctx\n@@ -0,0 +1 @@\n+def function(RIGHT, arg) do\ndiff --git a/t/t4018/elixir-macro.ctx b/t/t4018/elixir-macro.ctx\nnew file mode 100644\nindex 0000000000..fc1d3b85e8\n--- /dev/null\n+++ b/t/t4018/elixir-macro.ctx\n@@ -0,0 +1 @@\n+defmacro foo(RIGHT) do\ndiff --git a/t/t4018/elixir-module-func.ctx b/t/t4018/elixir-module-func.ctx\nnew file mode 100644\nindex 0000000000..8239214386\n--- /dev/null\n+++ b/t/t4018/elixir-module-func.ctx\n@@ -0,0 +1 @@\n+def fun(RIGHT) do\ndiff --git a/t/t4018/elixir-module.ctx b/t/t4018/elixir-module.ctx\nnew file mode 100644\nindex 0000000000..8f28a7a689\n--- /dev/null\n+++ b/t/t4018/elixir-module.ctx\n@@ -0,0 +1 @@\n+defmodule RIGHT do\ndiff --git a/t/t4018/elixir-nested-module.ctx b/t/t4018/elixir-nested-module.ctx\nnew file mode 100644\nindex 0000000000..3ffbdd18b1\n--- /dev/null\n+++ b/t/t4018/elixir-nested-module.ctx\n@@ -0,0 +1 @@\n+defmodule MyApp.RIGHT do\ndiff --git a/t/t4018/elixir-private-function.ctx b/t/t4018/elixir-private-function.ctx\nnew file mode 100644\nindex 0000000000..1c4eba44f7\n--- /dev/null\n+++ b/t/t4018/elixir-private-function.ctx\n@@ -0,0 +1 @@\n+defp function(RIGHT, arg) do\ndiff --git a/t/t4018/elixir-protocol-implementation.ctx b/t/t4018/elixir-protocol-implementation.ctx\nnew file mode 100644\nindex 0000000000..efb758aea6\n--- /dev/null\n+++ b/t/t4018/elixir-protocol-implementation.ctx\n@@ -0,0 +1 @@\n+defimpl RIGHT do\ndiff --git a/t/t4018/elixir-protocol.ctx b/t/t4018/elixir-protocol.ctx\nnew file mode 100644\nindex 0000000000..d0204e9f14\n--- /dev/null\n+++ b/t/t4018/elixir-protocol.ctx\n@@ -0,0 +1 @@\n+defprotocol RIGHT do\ndiff --git a/t/t4018/fortran-block-data.ctx b/t/t4018/fortran-block-data.ctx\nnew file mode 100644\nindex 0000000000..c3db084ccc\n--- /dev/null\n+++ b/t/t4018/fortran-block-data.ctx\n@@ -0,0 +1 @@\n+BLOCK DATA RIGHT\ndiff --git a/t/t4018/fortran-comment-keyword.ctx b/t/t4018/fortran-comment-keyword.ctx\nnew file mode 100644\nindex 0000000000..0b9220b355\n--- /dev/null\n+++ b/t/t4018/fortran-comment-keyword.ctx\n@@ -0,0 +1 @@\n+subroutine RIGHT (funcA, funcB)\ndiff --git a/t/t4018/fortran-comment-legacy-star.ctx b/t/t4018/fortran-comment-legacy-star.ctx\nnew file mode 100644\nindex 0000000000..6a34203f80\n--- /dev/null\n+++ b/t/t4018/fortran-comment-legacy-star.ctx\n@@ -0,0 +1 @@\n+subroutine RIGHT\ndiff --git a/t/t4018/fortran-comment-legacy.ctx b/t/t4018/fortran-comment-legacy.ctx\nnew file mode 100644\nindex 0000000000..6a34203f80\n--- /dev/null\n+++ b/t/t4018/fortran-comment-legacy.ctx\n@@ -0,0 +1 @@\n+subroutine RIGHT\ndiff --git a/t/t4018/fortran-comment.ctx b/t/t4018/fortran-comment.ctx\nnew file mode 100644\nindex 0000000000..6a34203f80\n--- /dev/null\n+++ b/t/t4018/fortran-comment.ctx\n@@ -0,0 +1 @@\n+subroutine RIGHT\ndiff --git a/t/t4018/fortran-external-function.ctx b/t/t4018/fortran-external-function.ctx\nnew file mode 100644\nindex 0000000000..56ec4d8eca\n--- /dev/null\n+++ b/t/t4018/fortran-external-function.ctx\n@@ -0,0 +1 @@\n+function RIGHT(a, b) result(c)\ndiff --git a/t/t4018/fortran-external-subroutine.ctx b/t/t4018/fortran-external-subroutine.ctx\nnew file mode 100644\nindex 0000000000..6a34203f80\n--- /dev/null\n+++ b/t/t4018/fortran-external-subroutine.ctx\n@@ -0,0 +1 @@\n+subroutine RIGHT\ndiff --git a/t/t4018/fortran-module-procedure.ctx b/t/t4018/fortran-module-procedure.ctx\nnew file mode 100644\nindex 0000000000..4f5ff2e4b8\n--- /dev/null\n+++ b/t/t4018/fortran-module-procedure.ctx\n@@ -0,0 +1 @@\n+module RIGHT\ndiff --git a/t/t4018/fortran-module.ctx b/t/t4018/fortran-module.ctx\nnew file mode 100644\nindex 0000000000..4f5ff2e4b8\n--- /dev/null\n+++ b/t/t4018/fortran-module.ctx\n@@ -0,0 +1 @@\n+module RIGHT\ndiff --git a/t/t4018/fortran-program.ctx b/t/t4018/fortran-program.ctx\nnew file mode 100644\nindex 0000000000..c4e844df30\n--- /dev/null\n+++ b/t/t4018/fortran-program.ctx\n@@ -0,0 +1 @@\n+program RIGHT\ndiff --git a/t/t4018/fountain-scene.ctx b/t/t4018/fountain-scene.ctx\nnew file mode 100644\nindex 0000000000..bf10171418\n--- /dev/null\n+++ b/t/t4018/fountain-scene.ctx\n@@ -0,0 +1 @@\n+EXT. STREET RIGHT OUTSIDE - DAY\ndiff --git a/t/t4018/golang-complex-function.ctx b/t/t4018/golang-complex-function.ctx\nnew file mode 100644\nindex 0000000000..8e8d5582ff\n--- /dev/null\n+++ b/t/t4018/golang-complex-function.ctx\n@@ -0,0 +1 @@\n+func (t *Test) RIGHT(a Type) (Type, error) {\ndiff --git a/t/t4018/golang-func.ctx b/t/t4018/golang-func.ctx\nnew file mode 100644\nindex 0000000000..88bc823813\n--- /dev/null\n+++ b/t/t4018/golang-func.ctx\n@@ -0,0 +1 @@\n+func RIGHT() {\ndiff --git a/t/t4018/golang-interface.ctx b/t/t4018/golang-interface.ctx\nnew file mode 100644\nindex 0000000000..2d07f5a383\n--- /dev/null\n+++ b/t/t4018/golang-interface.ctx\n@@ -0,0 +1 @@\n+type RIGHT interface {\ndiff --git a/t/t4018/golang-long-func.ctx b/t/t4018/golang-long-func.ctx\nnew file mode 100644\nindex 0000000000..25635e712e\n--- /dev/null\n+++ b/t/t4018/golang-long-func.ctx\n@@ -0,0 +1 @@\n+func RIGHT(aVeryVeryVeryLongVariableName AVeryVeryVeryLongType,\ndiff --git a/t/t4018/golang-struct.ctx b/t/t4018/golang-struct.ctx\nnew file mode 100644\nindex 0000000000..8a1240699d\n--- /dev/null\n+++ b/t/t4018/golang-struct.ctx\n@@ -0,0 +1 @@\n+type RIGHT struct {\ndiff --git a/t/t4018/java-class-member-function.ctx b/t/t4018/java-class-member-function.ctx\nnew file mode 100644\nindex 0000000000..2125474b68\n--- /dev/null\n+++ b/t/t4018/java-class-member-function.ctx\n@@ -0,0 +1 @@\n+public static void main(String RIGHT[])\ndiff --git a/t/t4018/markdown-heading-indented.ctx b/t/t4018/markdown-heading-indented.ctx\nnew file mode 100644\nindex 0000000000..5938336743\n--- /dev/null\n+++ b/t/t4018/markdown-heading-indented.ctx\n@@ -0,0 +1 @@\n+   ### RIGHT\ndiff --git a/t/t4018/markdown-heading-non-headings.ctx b/t/t4018/markdown-heading-non-headings.ctx\nnew file mode 100644\nindex 0000000000..7e2165be6e\n--- /dev/null\n+++ b/t/t4018/markdown-heading-non-headings.ctx\n@@ -0,0 +1 @@\n+# RIGHT\ndiff --git a/t/t4018/matlab-class-definition.ctx b/t/t4018/matlab-class-definition.ctx\nnew file mode 100644\nindex 0000000000..5dd5b45628\n--- /dev/null\n+++ b/t/t4018/matlab-class-definition.ctx\n@@ -0,0 +1 @@\n+classdef RIGHT\ndiff --git a/t/t4018/matlab-function.ctx b/t/t4018/matlab-function.ctx\nnew file mode 100644\nindex 0000000000..72d2350b13\n--- /dev/null\n+++ b/t/t4018/matlab-function.ctx\n@@ -0,0 +1 @@\n+function y = RIGHT()\ndiff --git a/t/t4018/matlab-octave-section-1.ctx b/t/t4018/matlab-octave-section-1.ctx\nnew file mode 100644\nindex 0000000000..ca9b349f94\n--- /dev/null\n+++ b/t/t4018/matlab-octave-section-1.ctx\n@@ -0,0 +1 @@\n+%%% RIGHT section\ndiff --git a/t/t4018/matlab-octave-section-2.ctx b/t/t4018/matlab-octave-section-2.ctx\nnew file mode 100644\nindex 0000000000..5cbb77faf5\n--- /dev/null\n+++ b/t/t4018/matlab-octave-section-2.ctx\n@@ -0,0 +1 @@\n+## RIGHT section\ndiff --git a/t/t4018/matlab-section.ctx b/t/t4018/matlab-section.ctx\nnew file mode 100644\nindex 0000000000..e83fee6f4d\n--- /dev/null\n+++ b/t/t4018/matlab-section.ctx\n@@ -0,0 +1 @@\n+%% RIGHT section\ndiff --git a/t/t4018/perl-skip-end-of-heredoc.ctx b/t/t4018/perl-skip-end-of-heredoc.ctx\nnew file mode 100644\nindex 0000000000..c15f4b78bd\n--- /dev/null\n+++ b/t/t4018/perl-skip-end-of-heredoc.ctx\n@@ -0,0 +1 @@\n+sub RIGHTwithheredocument {\ndiff --git a/t/t4018/perl-skip-forward-decl.ctx b/t/t4018/perl-skip-forward-decl.ctx\nnew file mode 100644\nindex 0000000000..e0c51599ad\n--- /dev/null\n+++ b/t/t4018/perl-skip-forward-decl.ctx\n@@ -0,0 +1 @@\n+package RIGHT;\ndiff --git a/t/t4018/perl-skip-sub-in-pod.ctx b/t/t4018/perl-skip-sub-in-pod.ctx\nnew file mode 100644\nindex 0000000000..abddd76655\n--- /dev/null\n+++ b/t/t4018/perl-skip-sub-in-pod.ctx\n@@ -0,0 +1 @@\n+=head1 SYNOPSIS_RIGHT\ndiff --git a/t/t4018/perl-sub-definition-kr-brace.ctx b/t/t4018/perl-sub-definition-kr-brace.ctx\nnew file mode 100644\nindex 0000000000..7e5aee5cde\n--- /dev/null\n+++ b/t/t4018/perl-sub-definition-kr-brace.ctx\n@@ -0,0 +1 @@\n+sub RIGHT\ndiff --git a/t/t4018/perl-sub-definition.ctx b/t/t4018/perl-sub-definition.ctx\nnew file mode 100644\nindex 0000000000..d49a63598e\n--- /dev/null\n+++ b/t/t4018/perl-sub-definition.ctx\n@@ -0,0 +1 @@\n+sub RIGHT {\ndiff --git a/t/t4018/php-abstract-class.ctx b/t/t4018/php-abstract-class.ctx\nnew file mode 100644\nindex 0000000000..f572d2129b\n--- /dev/null\n+++ b/t/t4018/php-abstract-class.ctx\n@@ -0,0 +1 @@\n+abstract class RIGHT\ndiff --git a/t/t4018/php-abstract-method.ctx b/t/t4018/php-abstract-method.ctx\nnew file mode 100644\nindex 0000000000..14cb6df42e\n--- /dev/null\n+++ b/t/t4018/php-abstract-method.ctx\n@@ -0,0 +1 @@\n+abstract public function RIGHT(): ?string\ndiff --git a/t/t4018/php-class.ctx b/t/t4018/php-class.ctx\nnew file mode 100644\nindex 0000000000..54bff816d6\n--- /dev/null\n+++ b/t/t4018/php-class.ctx\n@@ -0,0 +1 @@\n+class RIGHT\ndiff --git a/t/t4018/php-final-class.ctx b/t/t4018/php-final-class.ctx\nnew file mode 100644\nindex 0000000000..4d59fb749b\n--- /dev/null\n+++ b/t/t4018/php-final-class.ctx\n@@ -0,0 +1 @@\n+final class RIGHT\ndiff --git a/t/t4018/php-final-method.ctx b/t/t4018/php-final-method.ctx\nnew file mode 100644\nindex 0000000000..b7da8f8082\n--- /dev/null\n+++ b/t/t4018/php-final-method.ctx\n@@ -0,0 +1 @@\n+final public function RIGHT(): string\ndiff --git a/t/t4018/php-function.ctx b/t/t4018/php-function.ctx\nnew file mode 100644\nindex 0000000000..c5f3e55302\n--- /dev/null\n+++ b/t/t4018/php-function.ctx\n@@ -0,0 +1 @@\n+function RIGHT()\ndiff --git a/t/t4018/php-interface.ctx b/t/t4018/php-interface.ctx\nnew file mode 100644\nindex 0000000000..a45fa0532a\n--- /dev/null\n+++ b/t/t4018/php-interface.ctx\n@@ -0,0 +1 @@\n+interface RIGHT\ndiff --git a/t/t4018/php-method.ctx b/t/t4018/php-method.ctx\nnew file mode 100644\nindex 0000000000..eb1659ff9f\n--- /dev/null\n+++ b/t/t4018/php-method.ctx\n@@ -0,0 +1 @@\n+public static function RIGHT()\ndiff --git a/t/t4018/php-trait.ctx b/t/t4018/php-trait.ctx\nnew file mode 100644\nindex 0000000000..57aa4c6267\n--- /dev/null\n+++ b/t/t4018/php-trait.ctx\n@@ -0,0 +1 @@\n+trait RIGHT\ndiff --git a/t/t4018/python-async-def.ctx b/t/t4018/python-async-def.ctx\nnew file mode 100644\nindex 0000000000..468c548bbe\n--- /dev/null\n+++ b/t/t4018/python-async-def.ctx\n@@ -0,0 +1 @@\n+async def RIGHT(pi: int = 3.14):\ndiff --git a/t/t4018/python-class.ctx b/t/t4018/python-class.ctx\nnew file mode 100644\nindex 0000000000..a40b755e29\n--- /dev/null\n+++ b/t/t4018/python-class.ctx\n@@ -0,0 +1 @@\n+class RIGHT(int, str):\ndiff --git a/t/t4018/python-def.ctx b/t/t4018/python-def.ctx\nnew file mode 100644\nindex 0000000000..a1a9cbad63\n--- /dev/null\n+++ b/t/t4018/python-def.ctx\n@@ -0,0 +1 @@\n+def RIGHT(pi: int = 3.14):\ndiff --git a/t/t4018/python-indented-async-def.ctx b/t/t4018/python-indented-async-def.ctx\nnew file mode 100644\nindex 0000000000..d393620a1e\n--- /dev/null\n+++ b/t/t4018/python-indented-async-def.ctx\n@@ -0,0 +1 @@\n+async def RIGHT(self, x: int):\ndiff --git a/t/t4018/python-indented-class.ctx b/t/t4018/python-indented-class.ctx\nnew file mode 100644\nindex 0000000000..0881c84dba\n--- /dev/null\n+++ b/t/t4018/python-indented-class.ctx\n@@ -0,0 +1 @@\n+class RIGHT:\ndiff --git a/t/t4018/python-indented-def.ctx b/t/t4018/python-indented-def.ctx\nnew file mode 100644\nindex 0000000000..6e5a44b391\n--- /dev/null\n+++ b/t/t4018/python-indented-def.ctx\n@@ -0,0 +1 @@\n+def RIGHT(self, x: int):\ndiff --git a/t/t4018/rust-fn.ctx b/t/t4018/rust-fn.ctx\nnew file mode 100644\nindex 0000000000..baa37cf253\n--- /dev/null\n+++ b/t/t4018/rust-fn.ctx\n@@ -0,0 +1 @@\n+pub(self) fn RIGHT<T>(x: &[T]) where T: Debug {\ndiff --git a/t/t4018/rust-impl.ctx b/t/t4018/rust-impl.ctx\nnew file mode 100644\nindex 0000000000..5344c35f3f\n--- /dev/null\n+++ b/t/t4018/rust-impl.ctx\n@@ -0,0 +1 @@\n+impl<'a, T: AsRef<[u8]>>  std::RIGHT for Git<'a> {\ndiff --git a/t/t4018/rust-macro-rules.ctx b/t/t4018/rust-macro-rules.ctx\nnew file mode 100644\nindex 0000000000..7520463aa0\n--- /dev/null\n+++ b/t/t4018/rust-macro-rules.ctx\n@@ -0,0 +1 @@\n+macro_rules! RIGHT {\ndiff --git a/t/t4018/rust-struct.ctx b/t/t4018/rust-struct.ctx\nnew file mode 100644\nindex 0000000000..c1e09dc808\n--- /dev/null\n+++ b/t/t4018/rust-struct.ctx\n@@ -0,0 +1 @@\n+pub(super) struct RIGHT<'a> {\ndiff --git a/t/t4018/rust-trait.ctx b/t/t4018/rust-trait.ctx\nnew file mode 100644\nindex 0000000000..6af803db29\n--- /dev/null\n+++ b/t/t4018/rust-trait.ctx\n@@ -0,0 +1 @@\n+unsafe trait RIGHT<T> {\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416896","messageId":"20210215005236.11313-12-avarab@gmail.com","threadId":"55144","inReplyTo":"87tuqebj6m.fsf@evledraar.gmail.com","subject":"[PATCH 11/20] userdiff tests: remove hack for \"RIGHT\" token","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T00:52:27Z","receivedAt":"2021-02-15T00:54:41Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Now that the \"RIGHT\" token isn't how we select the desired hunk header\nline in the test anymore we can revert a hack added in\nf1b75fbaf1 (t4018: convert custom pattern test to the new\ninfrastructure, 2014-03-21) and go back to the regular expression we\nwere testing before that change.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018/custom.sh | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/t/t4018/custom.sh b/t/t4018/custom.sh\nindex 69f1f7339f..20abb38451 100755\n--- a/t/t4018/custom.sh\n+++ b/t/t4018/custom.sh\n@@ -33,14 +33,14 @@ public class Beer\n EOF_TEST\n \n test_expect_success 'custom: setup match to end of line' '\n-\tgit config diff.custom.funcname \"......Beer\\$\"\n+\tgit config diff.custom.funcname \"Beer\\$\"\n '\n \n test_diff_funcname 'custom: match to end of line' \\\n \t8<<\\EOF_HUNK 9<<\\EOF_TEST\n-RIGHT_Beer\n+Beer\n EOF_HUNK\n-public class RIGHT_Beer\n+public class Beer\n {\n \tint special;\n \tpublic static void main(String args[])\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416899","messageId":"20210215005236.11313-9-avarab@gmail.com","threadId":"55144","inReplyTo":"87tuqebj6m.fsf@evledraar.gmail.com","subject":"[PATCH 08/20] userdiff tests: rewrite hunk header test infrastructure","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T00:52:24Z","receivedAt":"2021-02-15T00:54:44Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Rewrite the hunk header test infrastructure introduced in\nbfa7d01413 (t4018: an infrastructure to test hunk headers,\n2014-03-21). See c228a5c077 (Merge branch 'js/userdiff-cc',\n2014-03-31) for the whole series that commit was part of.\n\nAs noted in an earlier commit that change introduced the regression of\nnot testing for the full hunk line, but just whether \"RIGHT\" appeared\non it[1]. A preceding commit fixed that specific issue, but we were\nstill left with the inflexibility of the approach described in the\nnow-deleted t/t4018/README.\n\nI.e. to add any sort of new tests that used the existing test data\nwe'd either need to add more files like the recently added (but now\ndeleted) *.ctx) files, using the filesystem as our test datastructure,\nor introduce more parsing for the custom file format we were growing\nhere.\n\nLet's instead just move this over to using a custom test\nfunction. This makes it trivial to add new tests by adding new\noptional parameters to the function. Let's still keep the relevant\nfiles in the \"t/t4018/\" subdirectory instead of adding ~1.5k\nlines (and growing) to \"t/t4018-diff-funcname.sh\"\n\nIf this diff is viewed with \"--color-moved=plain\" we can see that\nthere's no changes to the lines being moved into the new *.sh files,\ni.e. all the deletions are moves. I'm just adding boilerplate around\nthose existing lines.\n\nThe one-off refactoring was performed by an ad-hoc shellscript [2].\n\n1. https://lore.kernel.org/git/87wnvbbf2y.fsf@evledraar.gmail.com/\n2.\n\t#!/bin/sh\n\tset -ex\n\n\tgit rm README*\n\tfor t in $(git ls-files ':!*.ctx')\n\tdo\n\t\tlang=$(echo $t | sed 's/-.*//')\n\t\tdesc=$(echo $t | sed -E 's/^[^-]*-//' | tr - \" \")\n\n\t\tif ! test -e $lang.sh\n\t\tthen\n\t\t\tcat >$lang.sh <<-EOF\n\t\t\t#!/bin/sh\n\t\t\t#\n\t\t\t# See ../t4018-diff-funcname.sh's test_diff_funcname()\n\t\t\t#\n\n\t\t\tEOF\n\t\telse\n\t\t\techo >>$lang.sh\n\t        fi\n\n\t\t(\n\t            printf \"test_diff_funcname '%s: %s' \\\\\" \"$lang\" \"$desc\"\n\t            echo\n\t            printf \"\\t8<<%sEOF_HUNK 9<<%sEOF_TEST\\n\" '\\' '\\'\n\t            cat $t.ctx\n\t            printf \"EOF_HUNK\\n\"\n\t            cat $t\n\t            printf \"EOF_TEST\\n\"\n\t\t) >>$lang.sh\n\n\t\tchmod +x $lang.sh\n\t\tgit add $lang.sh\n\t        git rm $t $t.ctx\n\tdone\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018-diff-funcname.sh                      |  59 +++--\n t/t4018/README                                |  15 --\n t/t4018/README.ctx                            |   1 -\n t/t4018/bash-arithmetic-function              |   4 -\n t/t4018/bash-arithmetic-function.ctx          |   1 -\n t/t4018/bash-bashism-style-compact            |   6 -\n t/t4018/bash-bashism-style-compact.ctx        |   1 -\n t/t4018/bash-bashism-style-function           |   4 -\n t/t4018/bash-bashism-style-function.ctx       |   1 -\n t/t4018/bash-bashism-style-whitespace         |   4 -\n t/t4018/bash-bashism-style-whitespace.ctx     |   1 -\n t/t4018/bash-conditional-function             |   4 -\n t/t4018/bash-conditional-function.ctx         |   1 -\n t/t4018/bash-missing-parentheses              |   6 -\n t/t4018/bash-missing-parentheses.ctx          |   1 -\n t/t4018/bash-mixed-style-compact              |   4 -\n t/t4018/bash-mixed-style-compact.ctx          |   1 -\n t/t4018/bash-mixed-style-function             |   4 -\n t/t4018/bash-mixed-style-function.ctx         |   1 -\n t/t4018/bash-nested-functions                 |   6 -\n t/t4018/bash-nested-functions.ctx             |   1 -\n t/t4018/bash-other-characters                 |   4 -\n t/t4018/bash-other-characters.ctx             |   1 -\n t/t4018/bash-posix-style-compact              |   4 -\n t/t4018/bash-posix-style-compact.ctx          |   1 -\n t/t4018/bash-posix-style-function             |   4 -\n t/t4018/bash-posix-style-function.ctx         |   1 -\n t/t4018/bash-posix-style-whitespace           |   4 -\n t/t4018/bash-posix-style-whitespace.ctx       |   1 -\n t/t4018/bash-subshell-function                |   4 -\n t/t4018/bash-subshell-function.ctx            |   1 -\n t/t4018/bash-trailing-comment                 |   4 -\n t/t4018/bash-trailing-comment.ctx             |   1 -\n t/t4018/bash.sh                               | 160 ++++++++++++\n t/t4018/cpp-c++-function                      |   4 -\n t/t4018/cpp-c++-function.ctx                  |   1 -\n t/t4018/cpp-class-constructor                 |   4 -\n t/t4018/cpp-class-constructor-mem-init        |   5 -\n t/t4018/cpp-class-constructor-mem-init.ctx    |   1 -\n t/t4018/cpp-class-constructor.ctx             |   1 -\n t/t4018/cpp-class-definition                  |   4 -\n t/t4018/cpp-class-definition-derived          |   5 -\n t/t4018/cpp-class-definition-derived.ctx      |   1 -\n t/t4018/cpp-class-definition.ctx              |   1 -\n t/t4018/cpp-class-destructor                  |   4 -\n t/t4018/cpp-class-destructor.ctx              |   1 -\n t/t4018/cpp-function-returning-global-type    |   4 -\n .../cpp-function-returning-global-type.ctx    |   1 -\n t/t4018/cpp-function-returning-nested         |   5 -\n t/t4018/cpp-function-returning-nested.ctx     |   1 -\n t/t4018/cpp-function-returning-pointer        |   4 -\n t/t4018/cpp-function-returning-pointer.ctx    |   1 -\n t/t4018/cpp-function-returning-reference      |   4 -\n t/t4018/cpp-function-returning-reference.ctx  |   1 -\n t/t4018/cpp-gnu-style-function                |   5 -\n t/t4018/cpp-gnu-style-function.ctx            |   1 -\n t/t4018/cpp-namespace-definition              |   4 -\n t/t4018/cpp-namespace-definition.ctx          |   1 -\n t/t4018/cpp-operator-definition               |   4 -\n t/t4018/cpp-operator-definition.ctx           |   1 -\n t/t4018/cpp-skip-access-specifiers            |   8 -\n t/t4018/cpp-skip-access-specifiers.ctx        |   1 -\n t/t4018/cpp-skip-comment-block                |   9 -\n t/t4018/cpp-skip-comment-block.ctx            |   1 -\n t/t4018/cpp-skip-labels                       |   8 -\n t/t4018/cpp-skip-labels.ctx                   |   1 -\n t/t4018/cpp-struct-definition                 |   9 -\n t/t4018/cpp-struct-definition.ctx             |   1 -\n t/t4018/cpp-struct-single-line                |   7 -\n t/t4018/cpp-struct-single-line.ctx            |   1 -\n t/t4018/cpp-template-function-definition      |   4 -\n t/t4018/cpp-template-function-definition.ctx  |   1 -\n t/t4018/cpp-union-definition                  |   4 -\n t/t4018/cpp-union-definition.ctx              |   1 -\n t/t4018/cpp-void-c-function                   |   4 -\n t/t4018/cpp-void-c-function.ctx               |   1 -\n t/t4018/cpp.sh                                | 239 ++++++++++++++++++\n t/t4018/css-attribute-value-selector          |   4 -\n t/t4018/css-attribute-value-selector.ctx      |   1 -\n t/t4018/css-block-level-@-statements          |  10 -\n t/t4018/css-block-level-@-statements.ctx      |   1 -\n t/t4018/css-brace-in-col-1                    |   5 -\n t/t4018/css-brace-in-col-1.ctx                |   1 -\n t/t4018/css-class-selector                    |   4 -\n t/t4018/css-class-selector.ctx                |   1 -\n t/t4018/css-colon-eol                         |   4 -\n t/t4018/css-colon-eol.ctx                     |   1 -\n t/t4018/css-colon-selector                    |   5 -\n t/t4018/css-colon-selector.ctx                |   1 -\n t/t4018/css-common                            |   4 -\n t/t4018/css-common.ctx                        |   1 -\n t/t4018/css-id-selector                       |   4 -\n t/t4018/css-id-selector.ctx                   |   1 -\n t/t4018/css-long-selector-list                |   6 -\n t/t4018/css-long-selector-list.ctx            |   1 -\n t/t4018/css-prop-sans-indent                  |   5 -\n t/t4018/css-prop-sans-indent.ctx              |   1 -\n t/t4018/css-root-selector                     |   4 -\n t/t4018/css-root-selector.ctx                 |   1 -\n t/t4018/css-short-selector-list               |   4 -\n t/t4018/css-short-selector-list.ctx           |   1 -\n t/t4018/css-trailing-space                    |   5 -\n t/t4018/css-trailing-space.ctx                |   1 -\n t/t4018/css.sh                                | 146 +++++++++++\n t/t4018/custom1-pattern.ctx                   |   1 -\n t/t4018/{custom1-pattern => custom1.sh}       |  10 +\n t/t4018/custom2-match-to-end-of-line          |   8 -\n t/t4018/custom2-match-to-end-of-line.ctx      |   1 -\n t/t4018/custom2.sh                            |  18 ++\n t/t4018/custom3-alternation-in-pattern.ctx    |   1 -\n ...tom3-alternation-in-pattern => custom3.sh} |  10 +\n t/t4018/dts-labels                            |   9 -\n t/t4018/dts-labels.ctx                        |   1 -\n t/t4018/dts-node-unitless                     |   8 -\n t/t4018/dts-node-unitless.ctx                 |   1 -\n t/t4018/dts-nodes                             |   8 -\n t/t4018/dts-nodes-boolean-prop                |   9 -\n t/t4018/dts-nodes-boolean-prop.ctx            |   1 -\n t/t4018/dts-nodes-comment1                    |   8 -\n t/t4018/dts-nodes-comment1.ctx                |   1 -\n t/t4018/dts-nodes-comment2                    |   8 -\n t/t4018/dts-nodes-comment2.ctx                |   1 -\n t/t4018/dts-nodes-multiline-prop              |  13 -\n t/t4018/dts-nodes-multiline-prop.ctx          |   1 -\n t/t4018/dts-nodes.ctx                         |   1 -\n t/t4018/dts-reference                         |   9 -\n t/t4018/dts-reference.ctx                     |   1 -\n t/t4018/dts-root                              |   5 -\n t/t4018/dts-root-comment                      |   8 -\n t/t4018/dts-root-comment.ctx                  |   1 -\n t/t4018/dts-root.ctx                          |   1 -\n t/t4018/dts.sh                                | 149 +++++++++++\n t/t4018/elixir-do-not-pick-end                |   5 -\n t/t4018/elixir-do-not-pick-end.ctx            |   1 -\n t/t4018/elixir-ex-unit-test                   |   6 -\n t/t4018/elixir-ex-unit-test.ctx               |   1 -\n t/t4018/elixir-function                       |   5 -\n t/t4018/elixir-function.ctx                   |   1 -\n t/t4018/elixir-macro                          |   5 -\n t/t4018/elixir-macro.ctx                      |   1 -\n t/t4018/elixir-module                         |   9 -\n t/t4018/elixir-module-func                    |   8 -\n t/t4018/elixir-module-func.ctx                |   1 -\n t/t4018/elixir-module.ctx                     |   1 -\n t/t4018/elixir-nested-module                  |   9 -\n t/t4018/elixir-nested-module.ctx              |   1 -\n t/t4018/elixir-private-function               |   5 -\n t/t4018/elixir-private-function.ctx           |   1 -\n t/t4018/elixir-protocol                       |   6 -\n t/t4018/elixir-protocol-implementation        |   5 -\n t/t4018/elixir-protocol-implementation.ctx    |   1 -\n t/t4018/elixir-protocol.ctx                   |   1 -\n t/t4018/elixir.sh                             | 127 ++++++++++\n t/t4018/fortran-block-data                    |   5 -\n t/t4018/fortran-block-data.ctx                |   1 -\n t/t4018/fortran-comment                       |  13 -\n t/t4018/fortran-comment-keyword               |  14 -\n t/t4018/fortran-comment-keyword.ctx           |   1 -\n t/t4018/fortran-comment-legacy                |  13 -\n t/t4018/fortran-comment-legacy-star           |  13 -\n t/t4018/fortran-comment-legacy-star.ctx       |   1 -\n t/t4018/fortran-comment-legacy.ctx            |   1 -\n t/t4018/fortran-comment.ctx                   |   1 -\n t/t4018/fortran-external-function             |   9 -\n t/t4018/fortran-external-function.ctx         |   1 -\n t/t4018/fortran-external-subroutine           |   5 -\n t/t4018/fortran-external-subroutine.ctx       |   1 -\n t/t4018/fortran-module                        |   5 -\n t/t4018/fortran-module-procedure              |  13 -\n t/t4018/fortran-module-procedure.ctx          |   1 -\n t/t4018/fortran-module.ctx                    |   1 -\n t/t4018/fortran-program                       |   5 -\n t/t4018/fortran-program.ctx                   |   1 -\n t/t4018/fortran.sh                            | 159 ++++++++++++\n t/t4018/fountain-scene                        |   4 -\n t/t4018/fountain-scene.ctx                    |   1 -\n t/t4018/fountain.sh                           |  14 +\n t/t4018/golang-complex-function               |   8 -\n t/t4018/golang-complex-function.ctx           |   1 -\n t/t4018/golang-func                           |   4 -\n t/t4018/golang-func.ctx                       |   1 -\n t/t4018/golang-interface                      |   4 -\n t/t4018/golang-interface.ctx                  |   1 -\n t/t4018/golang-long-func                      |   5 -\n t/t4018/golang-long-func.ctx                  |   1 -\n t/t4018/golang-struct                         |   4 -\n t/t4018/golang-struct.ctx                     |   1 -\n t/t4018/golang.sh                             |  59 +++++\n t/t4018/java-class-member-function            |   8 -\n t/t4018/java-class-member-function.ctx        |   1 -\n t/t4018/java.sh                               |  18 ++\n t/t4018/markdown-heading-indented             |   6 -\n t/t4018/markdown-heading-indented.ctx         |   1 -\n t/t4018/markdown-heading-non-headings         |  17 --\n t/t4018/markdown-heading-non-headings.ctx     |   1 -\n t/t4018/markdown.sh                           |  39 +++\n t/t4018/matlab-class-definition               |   5 -\n t/t4018/matlab-class-definition.ctx           |   1 -\n t/t4018/matlab-function                       |   4 -\n t/t4018/matlab-function.ctx                   |   1 -\n t/t4018/matlab-octave-section-1               |   3 -\n t/t4018/matlab-octave-section-1.ctx           |   1 -\n t/t4018/matlab-octave-section-2               |   3 -\n t/t4018/matlab-octave-section-2.ctx           |   1 -\n t/t4018/matlab-section                        |   3 -\n t/t4018/matlab-section.ctx                    |   1 -\n t/t4018/matlab.sh                             |  52 ++++\n t/t4018/perl-skip-end-of-heredoc              |   8 -\n t/t4018/perl-skip-end-of-heredoc.ctx          |   1 -\n t/t4018/perl-skip-forward-decl                |  10 -\n t/t4018/perl-skip-forward-decl.ctx            |   1 -\n t/t4018/perl-skip-sub-in-pod                  |  18 --\n t/t4018/perl-skip-sub-in-pod.ctx              |   1 -\n t/t4018/perl-sub-definition                   |   4 -\n t/t4018/perl-sub-definition-kr-brace          |   4 -\n t/t4018/perl-sub-definition-kr-brace.ctx      |   1 -\n t/t4018/perl-sub-definition.ctx               |   1 -\n t/t4018/perl.sh                               |  78 ++++++\n t/t4018/php-abstract-class                    |   4 -\n t/t4018/php-abstract-class.ctx                |   1 -\n t/t4018/php-abstract-method                   |   7 -\n t/t4018/php-abstract-method.ctx               |   1 -\n t/t4018/php-class                             |   4 -\n t/t4018/php-class.ctx                         |   1 -\n t/t4018/php-final-class                       |   4 -\n t/t4018/php-final-class.ctx                   |   1 -\n t/t4018/php-final-method                      |   7 -\n t/t4018/php-final-method.ctx                  |   1 -\n t/t4018/php-function                          |   4 -\n t/t4018/php-function.ctx                      |   1 -\n t/t4018/php-interface                         |   4 -\n t/t4018/php-interface.ctx                     |   1 -\n t/t4018/php-method                            |   7 -\n t/t4018/php-method.ctx                        |   1 -\n t/t4018/php-trait                             |   7 -\n t/t4018/php-trait.ctx                         |   1 -\n t/t4018/php.sh                                | 106 ++++++++\n t/t4018/python-async-def                      |   4 -\n t/t4018/python-async-def.ctx                  |   1 -\n t/t4018/python-class                          |   4 -\n t/t4018/python-class.ctx                      |   1 -\n t/t4018/python-def                            |   4 -\n t/t4018/python-def.ctx                        |   1 -\n t/t4018/python-indented-async-def             |   7 -\n t/t4018/python-indented-async-def.ctx         |   1 -\n t/t4018/python-indented-class                 |   5 -\n t/t4018/python-indented-class.ctx             |   1 -\n t/t4018/python-indented-def                   |   7 -\n t/t4018/python-indented-def.ctx               |   1 -\n t/t4018/python.sh                             |  71 ++++++\n t/t4018/rust-fn                               |   5 -\n t/t4018/rust-fn.ctx                           |   1 -\n t/t4018/rust-impl                             |   5 -\n t/t4018/rust-impl.ctx                         |   1 -\n t/t4018/rust-macro-rules                      |   6 -\n t/t4018/rust-macro-rules.ctx                  |   1 -\n t/t4018/rust-struct                           |   5 -\n t/t4018/rust-struct.ctx                       |   1 -\n t/t4018/rust-trait                            |   5 -\n t/t4018/rust-trait.ctx                        |   1 -\n t/t4018/rust.sh                               |  60 +++++\n 261 files changed, 1549 insertions(+), 879 deletions(-)\n delete mode 100644 t/t4018/README\n delete mode 100644 t/t4018/README.ctx\n delete mode 100644 t/t4018/bash-arithmetic-function\n delete mode 100644 t/t4018/bash-arithmetic-function.ctx\n delete mode 100644 t/t4018/bash-bashism-style-compact\n delete mode 100644 t/t4018/bash-bashism-style-compact.ctx\n delete mode 100644 t/t4018/bash-bashism-style-function\n delete mode 100644 t/t4018/bash-bashism-style-function.ctx\n delete mode 100644 t/t4018/bash-bashism-style-whitespace\n delete mode 100644 t/t4018/bash-bashism-style-whitespace.ctx\n delete mode 100644 t/t4018/bash-conditional-function\n delete mode 100644 t/t4018/bash-conditional-function.ctx\n delete mode 100644 t/t4018/bash-missing-parentheses\n delete mode 100644 t/t4018/bash-missing-parentheses.ctx\n delete mode 100644 t/t4018/bash-mixed-style-compact\n delete mode 100644 t/t4018/bash-mixed-style-compact.ctx\n delete mode 100644 t/t4018/bash-mixed-style-function\n delete mode 100644 t/t4018/bash-mixed-style-function.ctx\n delete mode 100644 t/t4018/bash-nested-functions\n delete mode 100644 t/t4018/bash-nested-functions.ctx\n delete mode 100644 t/t4018/bash-other-characters\n delete mode 100644 t/t4018/bash-other-characters.ctx\n delete mode 100644 t/t4018/bash-posix-style-compact\n delete mode 100644 t/t4018/bash-posix-style-compact.ctx\n delete mode 100644 t/t4018/bash-posix-style-function\n delete mode 100644 t/t4018/bash-posix-style-function.ctx\n delete mode 100644 t/t4018/bash-posix-style-whitespace\n delete mode 100644 t/t4018/bash-posix-style-whitespace.ctx\n delete mode 100644 t/t4018/bash-subshell-function\n delete mode 100644 t/t4018/bash-subshell-function.ctx\n delete mode 100644 t/t4018/bash-trailing-comment\n delete mode 100644 t/t4018/bash-trailing-comment.ctx\n create mode 100755 t/t4018/bash.sh\n delete mode 100644 t/t4018/cpp-c++-function\n delete mode 100644 t/t4018/cpp-c++-function.ctx\n delete mode 100644 t/t4018/cpp-class-constructor\n delete mode 100644 t/t4018/cpp-class-constructor-mem-init\n delete mode 100644 t/t4018/cpp-class-constructor-mem-init.ctx\n delete mode 100644 t/t4018/cpp-class-constructor.ctx\n delete mode 100644 t/t4018/cpp-class-definition\n delete mode 100644 t/t4018/cpp-class-definition-derived\n delete mode 100644 t/t4018/cpp-class-definition-derived.ctx\n delete mode 100644 t/t4018/cpp-class-definition.ctx\n delete mode 100644 t/t4018/cpp-class-destructor\n delete mode 100644 t/t4018/cpp-class-destructor.ctx\n delete mode 100644 t/t4018/cpp-function-returning-global-type\n delete mode 100644 t/t4018/cpp-function-returning-global-type.ctx\n delete mode 100644 t/t4018/cpp-function-returning-nested\n delete mode 100644 t/t4018/cpp-function-returning-nested.ctx\n delete mode 100644 t/t4018/cpp-function-returning-pointer\n delete mode 100644 t/t4018/cpp-function-returning-pointer.ctx\n delete mode 100644 t/t4018/cpp-function-returning-reference\n delete mode 100644 t/t4018/cpp-function-returning-reference.ctx\n delete mode 100644 t/t4018/cpp-gnu-style-function\n delete mode 100644 t/t4018/cpp-gnu-style-function.ctx\n delete mode 100644 t/t4018/cpp-namespace-definition\n delete mode 100644 t/t4018/cpp-namespace-definition.ctx\n delete mode 100644 t/t4018/cpp-operator-definition\n delete mode 100644 t/t4018/cpp-operator-definition.ctx\n delete mode 100644 t/t4018/cpp-skip-access-specifiers\n delete mode 100644 t/t4018/cpp-skip-access-specifiers.ctx\n delete mode 100644 t/t4018/cpp-skip-comment-block\n delete mode 100644 t/t4018/cpp-skip-comment-block.ctx\n delete mode 100644 t/t4018/cpp-skip-labels\n delete mode 100644 t/t4018/cpp-skip-labels.ctx\n delete mode 100644 t/t4018/cpp-struct-definition\n delete mode 100644 t/t4018/cpp-struct-definition.ctx\n delete mode 100644 t/t4018/cpp-struct-single-line\n delete mode 100644 t/t4018/cpp-struct-single-line.ctx\n delete mode 100644 t/t4018/cpp-template-function-definition\n delete mode 100644 t/t4018/cpp-template-function-definition.ctx\n delete mode 100644 t/t4018/cpp-union-definition\n delete mode 100644 t/t4018/cpp-union-definition.ctx\n delete mode 100644 t/t4018/cpp-void-c-function\n delete mode 100644 t/t4018/cpp-void-c-function.ctx\n create mode 100755 t/t4018/cpp.sh\n delete mode 100644 t/t4018/css-attribute-value-selector\n delete mode 100644 t/t4018/css-attribute-value-selector.ctx\n delete mode 100644 t/t4018/css-block-level-@-statements\n delete mode 100644 t/t4018/css-block-level-@-statements.ctx\n delete mode 100644 t/t4018/css-brace-in-col-1\n delete mode 100644 t/t4018/css-brace-in-col-1.ctx\n delete mode 100644 t/t4018/css-class-selector\n delete mode 100644 t/t4018/css-class-selector.ctx\n delete mode 100644 t/t4018/css-colon-eol\n delete mode 100644 t/t4018/css-colon-eol.ctx\n delete mode 100644 t/t4018/css-colon-selector\n delete mode 100644 t/t4018/css-colon-selector.ctx\n delete mode 100644 t/t4018/css-common\n delete mode 100644 t/t4018/css-common.ctx\n delete mode 100644 t/t4018/css-id-selector\n delete mode 100644 t/t4018/css-id-selector.ctx\n delete mode 100644 t/t4018/css-long-selector-list\n delete mode 100644 t/t4018/css-long-selector-list.ctx\n delete mode 100644 t/t4018/css-prop-sans-indent\n delete mode 100644 t/t4018/css-prop-sans-indent.ctx\n delete mode 100644 t/t4018/css-root-selector\n delete mode 100644 t/t4018/css-root-selector.ctx\n delete mode 100644 t/t4018/css-short-selector-list\n delete mode 100644 t/t4018/css-short-selector-list.ctx\n delete mode 100644 t/t4018/css-trailing-space\n delete mode 100644 t/t4018/css-trailing-space.ctx\n create mode 100755 t/t4018/css.sh\n delete mode 100644 t/t4018/custom1-pattern.ctx\n rename t/t4018/{custom1-pattern => custom1.sh} (71%)\n mode change 100644 => 100755\n delete mode 100644 t/t4018/custom2-match-to-end-of-line\n delete mode 100644 t/t4018/custom2-match-to-end-of-line.ctx\n create mode 100755 t/t4018/custom2.sh\n delete mode 100644 t/t4018/custom3-alternation-in-pattern.ctx\n rename t/t4018/{custom3-alternation-in-pattern => custom3.sh} (66%)\n mode change 100644 => 100755\n delete mode 100644 t/t4018/dts-labels\n delete mode 100644 t/t4018/dts-labels.ctx\n delete mode 100644 t/t4018/dts-node-unitless\n delete mode 100644 t/t4018/dts-node-unitless.ctx\n delete mode 100644 t/t4018/dts-nodes\n delete mode 100644 t/t4018/dts-nodes-boolean-prop\n delete mode 100644 t/t4018/dts-nodes-boolean-prop.ctx\n delete mode 100644 t/t4018/dts-nodes-comment1\n delete mode 100644 t/t4018/dts-nodes-comment1.ctx\n delete mode 100644 t/t4018/dts-nodes-comment2\n delete mode 100644 t/t4018/dts-nodes-comment2.ctx\n delete mode 100644 t/t4018/dts-nodes-multiline-prop\n delete mode 100644 t/t4018/dts-nodes-multiline-prop.ctx\n delete mode 100644 t/t4018/dts-nodes.ctx\n delete mode 100644 t/t4018/dts-reference\n delete mode 100644 t/t4018/dts-reference.ctx\n delete mode 100644 t/t4018/dts-root\n delete mode 100644 t/t4018/dts-root-comment\n delete mode 100644 t/t4018/dts-root-comment.ctx\n delete mode 100644 t/t4018/dts-root.ctx\n create mode 100755 t/t4018/dts.sh\n delete mode 100644 t/t4018/elixir-do-not-pick-end\n delete mode 100644 t/t4018/elixir-do-not-pick-end.ctx\n delete mode 100644 t/t4018/elixir-ex-unit-test\n delete mode 100644 t/t4018/elixir-ex-unit-test.ctx\n delete mode 100644 t/t4018/elixir-function\n delete mode 100644 t/t4018/elixir-function.ctx\n delete mode 100644 t/t4018/elixir-macro\n delete mode 100644 t/t4018/elixir-macro.ctx\n delete mode 100644 t/t4018/elixir-module\n delete mode 100644 t/t4018/elixir-module-func\n delete mode 100644 t/t4018/elixir-module-func.ctx\n delete mode 100644 t/t4018/elixir-module.ctx\n delete mode 100644 t/t4018/elixir-nested-module\n delete mode 100644 t/t4018/elixir-nested-module.ctx\n delete mode 100644 t/t4018/elixir-private-function\n delete mode 100644 t/t4018/elixir-private-function.ctx\n delete mode 100644 t/t4018/elixir-protocol\n delete mode 100644 t/t4018/elixir-protocol-implementation\n delete mode 100644 t/t4018/elixir-protocol-implementation.ctx\n delete mode 100644 t/t4018/elixir-protocol.ctx\n create mode 100755 t/t4018/elixir.sh\n delete mode 100644 t/t4018/fortran-block-data\n delete mode 100644 t/t4018/fortran-block-data.ctx\n delete mode 100644 t/t4018/fortran-comment\n delete mode 100644 t/t4018/fortran-comment-keyword\n delete mode 100644 t/t4018/fortran-comment-keyword.ctx\n delete mode 100644 t/t4018/fortran-comment-legacy\n delete mode 100644 t/t4018/fortran-comment-legacy-star\n delete mode 100644 t/t4018/fortran-comment-legacy-star.ctx\n delete mode 100644 t/t4018/fortran-comment-legacy.ctx\n delete mode 100644 t/t4018/fortran-comment.ctx\n delete mode 100644 t/t4018/fortran-external-function\n delete mode 100644 t/t4018/fortran-external-function.ctx\n delete mode 100644 t/t4018/fortran-external-subroutine\n delete mode 100644 t/t4018/fortran-external-subroutine.ctx\n delete mode 100644 t/t4018/fortran-module\n delete mode 100644 t/t4018/fortran-module-procedure\n delete mode 100644 t/t4018/fortran-module-procedure.ctx\n delete mode 100644 t/t4018/fortran-module.ctx\n delete mode 100644 t/t4018/fortran-program\n delete mode 100644 t/t4018/fortran-program.ctx\n create mode 100755 t/t4018/fortran.sh\n delete mode 100644 t/t4018/fountain-scene\n delete mode 100644 t/t4018/fountain-scene.ctx\n create mode 100755 t/t4018/fountain.sh\n delete mode 100644 t/t4018/golang-complex-function\n delete mode 100644 t/t4018/golang-complex-function.ctx\n delete mode 100644 t/t4018/golang-func\n delete mode 100644 t/t4018/golang-func.ctx\n delete mode 100644 t/t4018/golang-interface\n delete mode 100644 t/t4018/golang-interface.ctx\n delete mode 100644 t/t4018/golang-long-func\n delete mode 100644 t/t4018/golang-long-func.ctx\n delete mode 100644 t/t4018/golang-struct\n delete mode 100644 t/t4018/golang-struct.ctx\n create mode 100755 t/t4018/golang.sh\n delete mode 100644 t/t4018/java-class-member-function\n delete mode 100644 t/t4018/java-class-member-function.ctx\n create mode 100755 t/t4018/java.sh\n delete mode 100644 t/t4018/markdown-heading-indented\n delete mode 100644 t/t4018/markdown-heading-indented.ctx\n delete mode 100644 t/t4018/markdown-heading-non-headings\n delete mode 100644 t/t4018/markdown-heading-non-headings.ctx\n create mode 100755 t/t4018/markdown.sh\n delete mode 100644 t/t4018/matlab-class-definition\n delete mode 100644 t/t4018/matlab-class-definition.ctx\n delete mode 100644 t/t4018/matlab-function\n delete mode 100644 t/t4018/matlab-function.ctx\n delete mode 100644 t/t4018/matlab-octave-section-1\n delete mode 100644 t/t4018/matlab-octave-section-1.ctx\n delete mode 100644 t/t4018/matlab-octave-section-2\n delete mode 100644 t/t4018/matlab-octave-section-2.ctx\n delete mode 100644 t/t4018/matlab-section\n delete mode 100644 t/t4018/matlab-section.ctx\n create mode 100755 t/t4018/matlab.sh\n delete mode 100644 t/t4018/perl-skip-end-of-heredoc\n delete mode 100644 t/t4018/perl-skip-end-of-heredoc.ctx\n delete mode 100644 t/t4018/perl-skip-forward-decl\n delete mode 100644 t/t4018/perl-skip-forward-decl.ctx\n delete mode 100644 t/t4018/perl-skip-sub-in-pod\n delete mode 100644 t/t4018/perl-skip-sub-in-pod.ctx\n delete mode 100644 t/t4018/perl-sub-definition\n delete mode 100644 t/t4018/perl-sub-definition-kr-brace\n delete mode 100644 t/t4018/perl-sub-definition-kr-brace.ctx\n delete mode 100644 t/t4018/perl-sub-definition.ctx\n create mode 100755 t/t4018/perl.sh\n delete mode 100644 t/t4018/php-abstract-class\n delete mode 100644 t/t4018/php-abstract-class.ctx\n delete mode 100644 t/t4018/php-abstract-method\n delete mode 100644 t/t4018/php-abstract-method.ctx\n delete mode 100644 t/t4018/php-class\n delete mode 100644 t/t4018/php-class.ctx\n delete mode 100644 t/t4018/php-final-class\n delete mode 100644 t/t4018/php-final-class.ctx\n delete mode 100644 t/t4018/php-final-method\n delete mode 100644 t/t4018/php-final-method.ctx\n delete mode 100644 t/t4018/php-function\n delete mode 100644 t/t4018/php-function.ctx\n delete mode 100644 t/t4018/php-interface\n delete mode 100644 t/t4018/php-interface.ctx\n delete mode 100644 t/t4018/php-method\n delete mode 100644 t/t4018/php-method.ctx\n delete mode 100644 t/t4018/php-trait\n delete mode 100644 t/t4018/php-trait.ctx\n create mode 100755 t/t4018/php.sh\n delete mode 100644 t/t4018/python-async-def\n delete mode 100644 t/t4018/python-async-def.ctx\n delete mode 100644 t/t4018/python-class\n delete mode 100644 t/t4018/python-class.ctx\n delete mode 100644 t/t4018/python-def\n delete mode 100644 t/t4018/python-def.ctx\n delete mode 100644 t/t4018/python-indented-async-def\n delete mode 100644 t/t4018/python-indented-async-def.ctx\n delete mode 100644 t/t4018/python-indented-class\n delete mode 100644 t/t4018/python-indented-class.ctx\n delete mode 100644 t/t4018/python-indented-def\n delete mode 100644 t/t4018/python-indented-def.ctx\n create mode 100755 t/t4018/python.sh\n delete mode 100644 t/t4018/rust-fn\n delete mode 100644 t/t4018/rust-fn.ctx\n delete mode 100644 t/t4018/rust-impl\n delete mode 100644 t/t4018/rust-impl.ctx\n delete mode 100644 t/t4018/rust-macro-rules\n delete mode 100644 t/t4018/rust-macro-rules.ctx\n delete mode 100644 t/t4018/rust-struct\n delete mode 100644 t/t4018/rust-struct.ctx\n delete mode 100644 t/t4018/rust-trait\n delete mode 100644 t/t4018/rust-trait.ctx\n create mode 100755 t/t4018/rust.sh\n\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex d32c38ad1a..3ff34c13d7 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -61,33 +61,42 @@ test_expect_success 'last regexp must not be negated' '\n \ttest_i18ngrep \": Last expression must not be negated:\" msg\n '\n \n-test_expect_success 'setup hunk header tests' '\n-\tfor i in $diffpatterns\n-\tdo\n-\t\techo \"$i-* diff=$i\"\n-\tdone > .gitattributes &&\n-\n-\t# add all test files to the index\n-\t(\n-\t\tcd \"$TEST_DIRECTORY\"/t4018 &&\n-\t\tgit --git-dir=\"$TRASH_DIRECTORY/.git\" add .\n-\t) &&\n-\n-\t# place modified files in the worktree\n-\tfor i in $(git ls-files)\n-\tdo\n-\t\tsed -e \"s/ChangeMe/IWasChanged/\" <\"$TEST_DIRECTORY/t4018/$i\" >\"$i\" || return 1\n-\tdone\n-'\n+test_diff_funcname () {\n+\tdesc=$1\n+\tcat <&8 >arg.header &&\n+\tcat <&9 >arg.test &&\n+\twhat=$(cat arg.what) &&\n+\n+\ttest_expect_success \"setup: $desc\" '\n+\t\tcp arg.test \"$what\" &&\n+\t\tcp arg.header expected &&\n+\t\tgit add \"$what\" &&\n+\t\tsed -e \"s/ChangeMe/IWasChanged/\" <\"$what\" >tmp &&\n+\t\tmv tmp \"$what\"\n+\t' &&\n+\n+\ttest_expect_success \"$desc\" '\n+\t\tgit diff -U1 \"$what\" >diff &&\n+\t\tsed -n -e \"s/^.*@@\\( \\|$\\)//p\" <diff >actual &&\n+\t\ttest_cmp expected actual\n+\t'\n+}\n \n-# check each individual file\n-for i in $(git ls-files -- ':!*.ctx')\n+for what in $diffpatterns\n do\n-\ttest_expect_success \"hunk header: $i\" \"\n-\t\tgit diff -U1 $i >diff &&\n-\t\tsed -n -e 's/^.*@@\\( \\|$\\)//p' <diff >ctx &&\n-\t\ttest_cmp $i.ctx ctx\n-\t\"\n+\ttest=\"$TEST_DIRECTORY/t4018/$what.sh\"\n+\tif ! test -e \"$test\"\n+\tthen\n+\t\ttest_expect_failure \"$what: no tests\" 'false'\n+\t\tcontinue\n+\tfi &&\n+\n+\ttest_expect_success \"setup: hunk header for $what\" '\n+\t\techo \"$what diff=$what\" >.gitattributes &&\n+\t\techo \"$what\" >arg.what\n+\t' &&\n+\n+\t. \"$test\"\n done\n \n test_done\ndiff --git a/t/t4018/README b/t/t4018/README\ndeleted file mode 100644\nindex 2cccf8c950..0000000000\n--- a/t/t4018/README\n+++ /dev/null\n@@ -1,15 +0,0 @@\n-How to write test cases\n-=======================\n-\n-Create test cases called \"LANG-whatever\" in this directory, where\n-\"LANG\" is e.g. the userdiff driver name, where \"whatever\" is a brief\n-description of the test.\n-\n-Insert the word \"ChangeMe\" (exactly this form) at a distance of\n-at least two lines from the line that must appear in the hunk header.\n-\n-The text that must appear in the hunk header must contains the word\n-\"RIGHT\" by convention. The \"LANG-whatever.ctx\" file contains what we\n-expect to appear in the hunk header. We munged the start of the line\n-to \"@@ [...] @@\" for ease of not having to hardcode the line numbers\n-and offsets.\ndiff --git a/t/t4018/README.ctx b/t/t4018/README.ctx\ndeleted file mode 100644\nindex cd79384b04..0000000000\n--- a/t/t4018/README.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-description of the test.\ndiff --git a/t/t4018/bash-arithmetic-function b/t/t4018/bash-arithmetic-function\ndeleted file mode 100644\nindex c0b276cb50..0000000000\n--- a/t/t4018/bash-arithmetic-function\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-RIGHT() ((\n-\n-    ChangeMe = \"$x\" + \"$y\"\n-))\ndiff --git a/t/t4018/bash-arithmetic-function.ctx b/t/t4018/bash-arithmetic-function.ctx\ndeleted file mode 100644\nindex 811eac7d2f..0000000000\n--- a/t/t4018/bash-arithmetic-function.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT()\ndiff --git a/t/t4018/bash-bashism-style-compact b/t/t4018/bash-bashism-style-compact\ndeleted file mode 100644\nindex 1ca3126f61..0000000000\n--- a/t/t4018/bash-bashism-style-compact\n+++ /dev/null\n@@ -1,6 +0,0 @@\n-function RIGHT {\n-    function InvalidSyntax{\n-        :\n-        echo 'ChangeMe'\n-    }\n-}\ndiff --git a/t/t4018/bash-bashism-style-compact.ctx b/t/t4018/bash-bashism-style-compact.ctx\ndeleted file mode 100644\nindex 4f8eac48c6..0000000000\n--- a/t/t4018/bash-bashism-style-compact.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-function RIGHT {\ndiff --git a/t/t4018/bash-bashism-style-function b/t/t4018/bash-bashism-style-function\ndeleted file mode 100644\nindex f1de4fa831..0000000000\n--- a/t/t4018/bash-bashism-style-function\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-function RIGHT {\n-    :\n-    echo 'ChangeMe'\n-}\ndiff --git a/t/t4018/bash-bashism-style-function.ctx b/t/t4018/bash-bashism-style-function.ctx\ndeleted file mode 100644\nindex 4f8eac48c6..0000000000\n--- a/t/t4018/bash-bashism-style-function.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-function RIGHT {\ndiff --git a/t/t4018/bash-bashism-style-whitespace b/t/t4018/bash-bashism-style-whitespace\ndeleted file mode 100644\nindex ade85dd3a5..0000000000\n--- a/t/t4018/bash-bashism-style-whitespace\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-\t function \tRIGHT \t( \t) \t{\n-\n-\t    ChangeMe\n-\t }\ndiff --git a/t/t4018/bash-bashism-style-whitespace.ctx b/t/t4018/bash-bashism-style-whitespace.ctx\ndeleted file mode 100644\nindex 35dbd0220e..0000000000\n--- a/t/t4018/bash-bashism-style-whitespace.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-function \tRIGHT \t( \t) \t{\ndiff --git a/t/t4018/bash-conditional-function b/t/t4018/bash-conditional-function\ndeleted file mode 100644\nindex c5949e829b..0000000000\n--- a/t/t4018/bash-conditional-function\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-RIGHT() [[ \\\n-\n-    \"$a\" > \"$ChangeMe\"\n-]]\ndiff --git a/t/t4018/bash-conditional-function.ctx b/t/t4018/bash-conditional-function.ctx\ndeleted file mode 100644\nindex 811eac7d2f..0000000000\n--- a/t/t4018/bash-conditional-function.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT()\ndiff --git a/t/t4018/bash-missing-parentheses b/t/t4018/bash-missing-parentheses\ndeleted file mode 100644\nindex 8c8a05dd7a..0000000000\n--- a/t/t4018/bash-missing-parentheses\n+++ /dev/null\n@@ -1,6 +0,0 @@\n-function RIGHT {\n-    functionInvalidSyntax {\n-        :\n-        echo 'ChangeMe'\n-    }\n-}\ndiff --git a/t/t4018/bash-missing-parentheses.ctx b/t/t4018/bash-missing-parentheses.ctx\ndeleted file mode 100644\nindex 4f8eac48c6..0000000000\n--- a/t/t4018/bash-missing-parentheses.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-function RIGHT {\ndiff --git a/t/t4018/bash-mixed-style-compact b/t/t4018/bash-mixed-style-compact\ndeleted file mode 100644\nindex d9364cba67..0000000000\n--- a/t/t4018/bash-mixed-style-compact\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-function RIGHT(){\n-    :\n-    echo 'ChangeMe'\n-}\ndiff --git a/t/t4018/bash-mixed-style-compact.ctx b/t/t4018/bash-mixed-style-compact.ctx\ndeleted file mode 100644\nindex bba11074eb..0000000000\n--- a/t/t4018/bash-mixed-style-compact.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-function RIGHT(){\ndiff --git a/t/t4018/bash-mixed-style-function b/t/t4018/bash-mixed-style-function\ndeleted file mode 100644\nindex 555f9b2466..0000000000\n--- a/t/t4018/bash-mixed-style-function\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-function RIGHT() {\n-\n-    ChangeMe\n-}\ndiff --git a/t/t4018/bash-mixed-style-function.ctx b/t/t4018/bash-mixed-style-function.ctx\ndeleted file mode 100644\nindex 922b87a4aa..0000000000\n--- a/t/t4018/bash-mixed-style-function.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-function RIGHT() {\ndiff --git a/t/t4018/bash-nested-functions b/t/t4018/bash-nested-functions\ndeleted file mode 100644\nindex 2c9237ead4..0000000000\n--- a/t/t4018/bash-nested-functions\n+++ /dev/null\n@@ -1,6 +0,0 @@\n-outer() {\n-    RIGHT() {\n-        :\n-        echo 'ChangeMe'\n-    }\n-}\ndiff --git a/t/t4018/bash-nested-functions.ctx b/t/t4018/bash-nested-functions.ctx\ndeleted file mode 100644\nindex 811eac7d2f..0000000000\n--- a/t/t4018/bash-nested-functions.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT()\ndiff --git a/t/t4018/bash-other-characters b/t/t4018/bash-other-characters\ndeleted file mode 100644\nindex a3f390d525..0000000000\n--- a/t/t4018/bash-other-characters\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-_RIGHT_0n() {\n-\n-    ChangeMe\n-}\ndiff --git a/t/t4018/bash-other-characters.ctx b/t/t4018/bash-other-characters.ctx\ndeleted file mode 100644\nindex 6a55317fdf..0000000000\n--- a/t/t4018/bash-other-characters.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-_RIGHT_0n()\ndiff --git a/t/t4018/bash-posix-style-compact b/t/t4018/bash-posix-style-compact\ndeleted file mode 100644\nindex 045bd2029b..0000000000\n--- a/t/t4018/bash-posix-style-compact\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-RIGHT(){\n-\n-    ChangeMe\n-}\ndiff --git a/t/t4018/bash-posix-style-compact.ctx b/t/t4018/bash-posix-style-compact.ctx\ndeleted file mode 100644\nindex 811eac7d2f..0000000000\n--- a/t/t4018/bash-posix-style-compact.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT()\ndiff --git a/t/t4018/bash-posix-style-function b/t/t4018/bash-posix-style-function\ndeleted file mode 100644\nindex a4d144856e..0000000000\n--- a/t/t4018/bash-posix-style-function\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-RIGHT() {\n-\n-    ChangeMe\n-}\ndiff --git a/t/t4018/bash-posix-style-function.ctx b/t/t4018/bash-posix-style-function.ctx\ndeleted file mode 100644\nindex 811eac7d2f..0000000000\n--- a/t/t4018/bash-posix-style-function.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT()\ndiff --git a/t/t4018/bash-posix-style-whitespace b/t/t4018/bash-posix-style-whitespace\ndeleted file mode 100644\nindex 4d984f0aa4..0000000000\n--- a/t/t4018/bash-posix-style-whitespace\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-\t RIGHT \t( \t) \t{\n-\n-\t    ChangeMe\n-\t }\ndiff --git a/t/t4018/bash-posix-style-whitespace.ctx b/t/t4018/bash-posix-style-whitespace.ctx\ndeleted file mode 100644\nindex 28f8698e14..0000000000\n--- a/t/t4018/bash-posix-style-whitespace.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT \t( \t)\ndiff --git a/t/t4018/bash-subshell-function b/t/t4018/bash-subshell-function\ndeleted file mode 100644\nindex 80baa09484..0000000000\n--- a/t/t4018/bash-subshell-function\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-RIGHT() (\n-\n-    ChangeMe=2\n-)\ndiff --git a/t/t4018/bash-subshell-function.ctx b/t/t4018/bash-subshell-function.ctx\ndeleted file mode 100644\nindex 811eac7d2f..0000000000\n--- a/t/t4018/bash-subshell-function.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT()\ndiff --git a/t/t4018/bash-trailing-comment b/t/t4018/bash-trailing-comment\ndeleted file mode 100644\nindex f1edbeda31..0000000000\n--- a/t/t4018/bash-trailing-comment\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-RIGHT() { # Comment\n-\n-    ChangeMe\n-}\ndiff --git a/t/t4018/bash-trailing-comment.ctx b/t/t4018/bash-trailing-comment.ctx\ndeleted file mode 100644\nindex 811eac7d2f..0000000000\n--- a/t/t4018/bash-trailing-comment.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT()\ndiff --git a/t/t4018/bash.sh b/t/t4018/bash.sh\nnew file mode 100755\nindex 0000000000..69144d9144\n--- /dev/null\n+++ b/t/t4018/bash.sh\n@@ -0,0 +1,160 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'bash: arithmetic function' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT()\n+EOF_HUNK\n+RIGHT() ((\n+\n+    ChangeMe = \"$x\" + \"$y\"\n+))\n+EOF_TEST\n+\n+test_diff_funcname 'bash: bashism style compact' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+function RIGHT {\n+EOF_HUNK\n+function RIGHT {\n+    function InvalidSyntax{\n+        :\n+        echo 'ChangeMe'\n+    }\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'bash: bashism style function' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+function RIGHT {\n+EOF_HUNK\n+function RIGHT {\n+    :\n+    echo 'ChangeMe'\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'bash: bashism style whitespace' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+function \tRIGHT \t( \t) \t{\n+EOF_HUNK\n+\t function \tRIGHT \t( \t) \t{\n+\n+\t    ChangeMe\n+\t }\n+EOF_TEST\n+\n+test_diff_funcname 'bash: conditional function' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT()\n+EOF_HUNK\n+RIGHT() [[ \\\n+\n+    \"$a\" > \"$ChangeMe\"\n+]]\n+EOF_TEST\n+\n+test_diff_funcname 'bash: missing parentheses' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+function RIGHT {\n+EOF_HUNK\n+function RIGHT {\n+    functionInvalidSyntax {\n+        :\n+        echo 'ChangeMe'\n+    }\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'bash: mixed style compact' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+function RIGHT(){\n+EOF_HUNK\n+function RIGHT(){\n+    :\n+    echo 'ChangeMe'\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'bash: mixed style function' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+function RIGHT() {\n+EOF_HUNK\n+function RIGHT() {\n+\n+    ChangeMe\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'bash: nested functions' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT()\n+EOF_HUNK\n+outer() {\n+    RIGHT() {\n+        :\n+        echo 'ChangeMe'\n+    }\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'bash: other characters' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+_RIGHT_0n()\n+EOF_HUNK\n+_RIGHT_0n() {\n+\n+    ChangeMe\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'bash: posix style compact' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT()\n+EOF_HUNK\n+RIGHT(){\n+\n+    ChangeMe\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'bash: posix style function' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT()\n+EOF_HUNK\n+RIGHT() {\n+\n+    ChangeMe\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'bash: posix style whitespace' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT \t( \t)\n+EOF_HUNK\n+\t RIGHT \t( \t) \t{\n+\n+\t    ChangeMe\n+\t }\n+EOF_TEST\n+\n+test_diff_funcname 'bash: subshell function' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT()\n+EOF_HUNK\n+RIGHT() (\n+\n+    ChangeMe=2\n+)\n+EOF_TEST\n+\n+test_diff_funcname 'bash: trailing comment' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT()\n+EOF_HUNK\n+RIGHT() { # Comment\n+\n+    ChangeMe\n+}\n+EOF_TEST\ndiff --git a/t/t4018/cpp-c++-function b/t/t4018/cpp-c++-function\ndeleted file mode 100644\nindex 9ee6bbef55..0000000000\n--- a/t/t4018/cpp-c++-function\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-Item RIGHT::DoSomething( Args with_spaces )\n-{\n-\tChangeMe;\n-}\ndiff --git a/t/t4018/cpp-c++-function.ctx b/t/t4018/cpp-c++-function.ctx\ndeleted file mode 100644\nindex 337b49dbb3..0000000000\n--- a/t/t4018/cpp-c++-function.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-Item RIGHT::DoSomething( Args with_spaces )\ndiff --git a/t/t4018/cpp-class-constructor b/t/t4018/cpp-class-constructor\ndeleted file mode 100644\nindex ec4f115c25..0000000000\n--- a/t/t4018/cpp-class-constructor\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-Item::Item(int RIGHT)\n-{\n-\tChangeMe;\n-}\ndiff --git a/t/t4018/cpp-class-constructor-mem-init b/t/t4018/cpp-class-constructor-mem-init\ndeleted file mode 100644\nindex 49a69f37e1..0000000000\n--- a/t/t4018/cpp-class-constructor-mem-init\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-Item::Item(int RIGHT) :\n-\tmember(0)\n-{\n-\tChangeMe;\n-}\ndiff --git a/t/t4018/cpp-class-constructor-mem-init.ctx b/t/t4018/cpp-class-constructor-mem-init.ctx\ndeleted file mode 100644\nindex 6664b8b3d8..0000000000\n--- a/t/t4018/cpp-class-constructor-mem-init.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-Item::Item(int RIGHT) :\ndiff --git a/t/t4018/cpp-class-constructor.ctx b/t/t4018/cpp-class-constructor.ctx\ndeleted file mode 100644\nindex 2dcadfc0ba..0000000000\n--- a/t/t4018/cpp-class-constructor.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-Item::Item(int RIGHT)\ndiff --git a/t/t4018/cpp-class-definition b/t/t4018/cpp-class-definition\ndeleted file mode 100644\nindex 11b61da3b7..0000000000\n--- a/t/t4018/cpp-class-definition\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-class RIGHT\n-{\n-\tint ChangeMe;\n-};\ndiff --git a/t/t4018/cpp-class-definition-derived b/t/t4018/cpp-class-definition-derived\ndeleted file mode 100644\nindex 3b98cd09ab..0000000000\n--- a/t/t4018/cpp-class-definition-derived\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-class RIGHT :\n-\tpublic Baseclass\n-{\n-\tint ChangeMe;\n-};\ndiff --git a/t/t4018/cpp-class-definition-derived.ctx b/t/t4018/cpp-class-definition-derived.ctx\ndeleted file mode 100644\nindex 146f0a7b7c..0000000000\n--- a/t/t4018/cpp-class-definition-derived.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-class RIGHT :\ndiff --git a/t/t4018/cpp-class-definition.ctx b/t/t4018/cpp-class-definition.ctx\ndeleted file mode 100644\nindex 54bff816d6..0000000000\n--- a/t/t4018/cpp-class-definition.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-class RIGHT\ndiff --git a/t/t4018/cpp-class-destructor b/t/t4018/cpp-class-destructor\ndeleted file mode 100644\nindex 5487665096..0000000000\n--- a/t/t4018/cpp-class-destructor\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-RIGHT::~RIGHT()\n-{\n-\tChangeMe;\n-}\ndiff --git a/t/t4018/cpp-class-destructor.ctx b/t/t4018/cpp-class-destructor.ctx\ndeleted file mode 100644\nindex 5390c17cf6..0000000000\n--- a/t/t4018/cpp-class-destructor.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT::~RIGHT()\ndiff --git a/t/t4018/cpp-function-returning-global-type b/t/t4018/cpp-function-returning-global-type\ndeleted file mode 100644\nindex 1084d5990e..0000000000\n--- a/t/t4018/cpp-function-returning-global-type\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-::Item get::it::RIGHT()\n-{\n-\tChangeMe;\n-}\ndiff --git a/t/t4018/cpp-function-returning-global-type.ctx b/t/t4018/cpp-function-returning-global-type.ctx\ndeleted file mode 100644\nindex 4dcdde25f4..0000000000\n--- a/t/t4018/cpp-function-returning-global-type.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-::Item get::it::RIGHT()\ndiff --git a/t/t4018/cpp-function-returning-nested b/t/t4018/cpp-function-returning-nested\ndeleted file mode 100644\nindex d9750aa61a..0000000000\n--- a/t/t4018/cpp-function-returning-nested\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-get::Item get::it::RIGHT()\n-{\n-\tChangeMe;\n-}\n-\ndiff --git a/t/t4018/cpp-function-returning-nested.ctx b/t/t4018/cpp-function-returning-nested.ctx\ndeleted file mode 100644\nindex 6ef73c8368..0000000000\n--- a/t/t4018/cpp-function-returning-nested.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-get::Item get::it::RIGHT()\ndiff --git a/t/t4018/cpp-function-returning-pointer b/t/t4018/cpp-function-returning-pointer\ndeleted file mode 100644\nindex ef15657ea8..0000000000\n--- a/t/t4018/cpp-function-returning-pointer\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-const char *get_it_RIGHT(char *ptr)\n-{\n-\tChangeMe;\n-}\ndiff --git a/t/t4018/cpp-function-returning-pointer.ctx b/t/t4018/cpp-function-returning-pointer.ctx\ndeleted file mode 100644\nindex bb0acce5c7..0000000000\n--- a/t/t4018/cpp-function-returning-pointer.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-const char *get_it_RIGHT(char *ptr)\ndiff --git a/t/t4018/cpp-function-returning-reference b/t/t4018/cpp-function-returning-reference\ndeleted file mode 100644\nindex 01b051df70..0000000000\n--- a/t/t4018/cpp-function-returning-reference\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-string& get::it::RIGHT(char *ptr)\n-{\n-\tChangeMe;\n-}\ndiff --git a/t/t4018/cpp-function-returning-reference.ctx b/t/t4018/cpp-function-returning-reference.ctx\ndeleted file mode 100644\nindex 76afe381fd..0000000000\n--- a/t/t4018/cpp-function-returning-reference.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-string& get::it::RIGHT(char *ptr)\ndiff --git a/t/t4018/cpp-gnu-style-function b/t/t4018/cpp-gnu-style-function\ndeleted file mode 100644\nindex 08c7c7565a..0000000000\n--- a/t/t4018/cpp-gnu-style-function\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-const char *\n-RIGHT(int arg)\n-{\n-\tChangeMe;\n-}\ndiff --git a/t/t4018/cpp-gnu-style-function.ctx b/t/t4018/cpp-gnu-style-function.ctx\ndeleted file mode 100644\nindex 1858287812..0000000000\n--- a/t/t4018/cpp-gnu-style-function.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT(int arg)\ndiff --git a/t/t4018/cpp-namespace-definition b/t/t4018/cpp-namespace-definition\ndeleted file mode 100644\nindex 6749980241..0000000000\n--- a/t/t4018/cpp-namespace-definition\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-namespace RIGHT\n-{\n-\tChangeMe;\n-}\ndiff --git a/t/t4018/cpp-namespace-definition.ctx b/t/t4018/cpp-namespace-definition.ctx\ndeleted file mode 100644\nindex 14c29c4638..0000000000\n--- a/t/t4018/cpp-namespace-definition.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-namespace RIGHT\ndiff --git a/t/t4018/cpp-operator-definition b/t/t4018/cpp-operator-definition\ndeleted file mode 100644\nindex 1acd827159..0000000000\n--- a/t/t4018/cpp-operator-definition\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-Value operator+(Value LEFT, Value RIGHT)\n-{\n-\tChangeMe;\n-}\ndiff --git a/t/t4018/cpp-operator-definition.ctx b/t/t4018/cpp-operator-definition.ctx\ndeleted file mode 100644\nindex 5b56778961..0000000000\n--- a/t/t4018/cpp-operator-definition.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-Value operator+(Value LEFT, Value RIGHT)\ndiff --git a/t/t4018/cpp-skip-access-specifiers b/t/t4018/cpp-skip-access-specifiers\ndeleted file mode 100644\nindex 4d4a9dbb9d..0000000000\n--- a/t/t4018/cpp-skip-access-specifiers\n+++ /dev/null\n@@ -1,8 +0,0 @@\n-class RIGHT : public Baseclass\n-{\n-public:\n-protected:\n-private:\n-\tvoid DoSomething();\n-\tint ChangeMe;\n-};\ndiff --git a/t/t4018/cpp-skip-access-specifiers.ctx b/t/t4018/cpp-skip-access-specifiers.ctx\ndeleted file mode 100644\nindex 075bcd883b..0000000000\n--- a/t/t4018/cpp-skip-access-specifiers.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-class RIGHT : public Baseclass\ndiff --git a/t/t4018/cpp-skip-comment-block b/t/t4018/cpp-skip-comment-block\ndeleted file mode 100644\nindex 3800b9967a..0000000000\n--- a/t/t4018/cpp-skip-comment-block\n+++ /dev/null\n@@ -1,9 +0,0 @@\n-struct item RIGHT(int i)\n-// Do not\n-// pick up\n-/* these\n-** comments.\n-*/\n-{\n-\tChangeMe;\n-}\ndiff --git a/t/t4018/cpp-skip-comment-block.ctx b/t/t4018/cpp-skip-comment-block.ctx\ndeleted file mode 100644\nindex 656c59893d..0000000000\n--- a/t/t4018/cpp-skip-comment-block.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-struct item RIGHT(int i)\ndiff --git a/t/t4018/cpp-skip-labels b/t/t4018/cpp-skip-labels\ndeleted file mode 100644\nindex b9c10aba22..0000000000\n--- a/t/t4018/cpp-skip-labels\n+++ /dev/null\n@@ -1,8 +0,0 @@\n-void RIGHT (void)\n-{\n-repeat:\t\t// C++ comment\n-next:\t\t/* C comment */\n-\tdo_something();\n-\n-\tChangeMe;\n-}\ndiff --git a/t/t4018/cpp-skip-labels.ctx b/t/t4018/cpp-skip-labels.ctx\ndeleted file mode 100644\nindex 6b0635f7f7..0000000000\n--- a/t/t4018/cpp-skip-labels.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-void RIGHT (void)\ndiff --git a/t/t4018/cpp-struct-definition b/t/t4018/cpp-struct-definition\ndeleted file mode 100644\nindex 521c59fd15..0000000000\n--- a/t/t4018/cpp-struct-definition\n+++ /dev/null\n@@ -1,9 +0,0 @@\n-struct RIGHT {\n-\tunsigned\n-\t/* this bit field looks like a label and should not be picked up */\n-\t\tdecoy_bitfield: 2,\n-\t\tmore : 1;\n-\tint filler;\n-\n-\tint ChangeMe;\n-};\ndiff --git a/t/t4018/cpp-struct-definition.ctx b/t/t4018/cpp-struct-definition.ctx\ndeleted file mode 100644\nindex 48ed893279..0000000000\n--- a/t/t4018/cpp-struct-definition.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-struct RIGHT {\ndiff --git a/t/t4018/cpp-struct-single-line b/t/t4018/cpp-struct-single-line\ndeleted file mode 100644\nindex a0de5fb800..0000000000\n--- a/t/t4018/cpp-struct-single-line\n+++ /dev/null\n@@ -1,7 +0,0 @@\n-void wrong()\n-{\n-}\n-\n-struct RIGHT_iterator_tag {};\n-\n-int ChangeMe;\ndiff --git a/t/t4018/cpp-struct-single-line.ctx b/t/t4018/cpp-struct-single-line.ctx\ndeleted file mode 100644\nindex e3bc9d5017..0000000000\n--- a/t/t4018/cpp-struct-single-line.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-struct RIGHT_iterator_tag {};\ndiff --git a/t/t4018/cpp-template-function-definition b/t/t4018/cpp-template-function-definition\ndeleted file mode 100644\nindex 0cdf5ba5bd..0000000000\n--- a/t/t4018/cpp-template-function-definition\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-template<class T> int RIGHT(T arg)\n-{\n-\tChangeMe;\n-}\ndiff --git a/t/t4018/cpp-template-function-definition.ctx b/t/t4018/cpp-template-function-definition.ctx\ndeleted file mode 100644\nindex c9da39cf65..0000000000\n--- a/t/t4018/cpp-template-function-definition.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-template<class T> int RIGHT(T arg)\ndiff --git a/t/t4018/cpp-union-definition b/t/t4018/cpp-union-definition\ndeleted file mode 100644\nindex 7ec94df697..0000000000\n--- a/t/t4018/cpp-union-definition\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-union RIGHT {\n-\tdouble v;\n-\tint ChangeMe;\n-};\ndiff --git a/t/t4018/cpp-union-definition.ctx b/t/t4018/cpp-union-definition.ctx\ndeleted file mode 100644\nindex 2fc7b54fb8..0000000000\n--- a/t/t4018/cpp-union-definition.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-union RIGHT {\ndiff --git a/t/t4018/cpp-void-c-function b/t/t4018/cpp-void-c-function\ndeleted file mode 100644\nindex 153081e872..0000000000\n--- a/t/t4018/cpp-void-c-function\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-void RIGHT (void)\n-{\n-\tChangeMe;\n-}\ndiff --git a/t/t4018/cpp-void-c-function.ctx b/t/t4018/cpp-void-c-function.ctx\ndeleted file mode 100644\nindex 6b0635f7f7..0000000000\n--- a/t/t4018/cpp-void-c-function.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-void RIGHT (void)\ndiff --git a/t/t4018/cpp.sh b/t/t4018/cpp.sh\nnew file mode 100755\nindex 0000000000..185d40d5ef\n--- /dev/null\n+++ b/t/t4018/cpp.sh\n@@ -0,0 +1,239 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'cpp: c++ function' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+Item RIGHT::DoSomething( Args with_spaces )\n+EOF_HUNK\n+Item RIGHT::DoSomething( Args with_spaces )\n+{\n+\tChangeMe;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'cpp: class constructor' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+Item::Item(int RIGHT)\n+EOF_HUNK\n+Item::Item(int RIGHT)\n+{\n+\tChangeMe;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'cpp: class constructor mem init' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+Item::Item(int RIGHT) :\n+EOF_HUNK\n+Item::Item(int RIGHT) :\n+\tmember(0)\n+{\n+\tChangeMe;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'cpp: class definition' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+class RIGHT\n+EOF_HUNK\n+class RIGHT\n+{\n+\tint ChangeMe;\n+};\n+EOF_TEST\n+\n+test_diff_funcname 'cpp: class definition derived' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+class RIGHT :\n+EOF_HUNK\n+class RIGHT :\n+\tpublic Baseclass\n+{\n+\tint ChangeMe;\n+};\n+EOF_TEST\n+\n+test_diff_funcname 'cpp: class destructor' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT::~RIGHT()\n+EOF_HUNK\n+RIGHT::~RIGHT()\n+{\n+\tChangeMe;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'cpp: function returning global type' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+::Item get::it::RIGHT()\n+EOF_HUNK\n+::Item get::it::RIGHT()\n+{\n+\tChangeMe;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'cpp: function returning nested' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+get::Item get::it::RIGHT()\n+EOF_HUNK\n+get::Item get::it::RIGHT()\n+{\n+\tChangeMe;\n+}\n+\n+EOF_TEST\n+\n+test_diff_funcname 'cpp: function returning pointer' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+const char *get_it_RIGHT(char *ptr)\n+EOF_HUNK\n+const char *get_it_RIGHT(char *ptr)\n+{\n+\tChangeMe;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'cpp: function returning reference' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+string& get::it::RIGHT(char *ptr)\n+EOF_HUNK\n+string& get::it::RIGHT(char *ptr)\n+{\n+\tChangeMe;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'cpp: gnu style function' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT(int arg)\n+EOF_HUNK\n+const char *\n+RIGHT(int arg)\n+{\n+\tChangeMe;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'cpp: namespace definition' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+namespace RIGHT\n+EOF_HUNK\n+namespace RIGHT\n+{\n+\tChangeMe;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'cpp: operator definition' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+Value operator+(Value LEFT, Value RIGHT)\n+EOF_HUNK\n+Value operator+(Value LEFT, Value RIGHT)\n+{\n+\tChangeMe;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'cpp: skip access specifiers' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+class RIGHT : public Baseclass\n+EOF_HUNK\n+class RIGHT : public Baseclass\n+{\n+public:\n+protected:\n+private:\n+\tvoid DoSomething();\n+\tint ChangeMe;\n+};\n+EOF_TEST\n+\n+test_diff_funcname 'cpp: skip comment block' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+struct item RIGHT(int i)\n+EOF_HUNK\n+struct item RIGHT(int i)\n+// Do not\n+// pick up\n+/* these\n+** comments.\n+*/\n+{\n+\tChangeMe;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'cpp: skip labels' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+void RIGHT (void)\n+EOF_HUNK\n+void RIGHT (void)\n+{\n+repeat:\t\t// C++ comment\n+next:\t\t/* C comment */\n+\tdo_something();\n+\n+\tChangeMe;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'cpp: struct definition' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+struct RIGHT {\n+EOF_HUNK\n+struct RIGHT {\n+\tunsigned\n+\t/* this bit field looks like a label and should not be picked up */\n+\t\tdecoy_bitfield: 2,\n+\t\tmore : 1;\n+\tint filler;\n+\n+\tint ChangeMe;\n+};\n+EOF_TEST\n+\n+test_diff_funcname 'cpp: struct single line' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+struct RIGHT_iterator_tag {};\n+EOF_HUNK\n+void wrong()\n+{\n+}\n+\n+struct RIGHT_iterator_tag {};\n+\n+int ChangeMe;\n+EOF_TEST\n+\n+test_diff_funcname 'cpp: template function definition' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+template<class T> int RIGHT(T arg)\n+EOF_HUNK\n+template<class T> int RIGHT(T arg)\n+{\n+\tChangeMe;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'cpp: union definition' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+union RIGHT {\n+EOF_HUNK\n+union RIGHT {\n+\tdouble v;\n+\tint ChangeMe;\n+};\n+EOF_TEST\n+\n+test_diff_funcname 'cpp: void c function' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+void RIGHT (void)\n+EOF_HUNK\n+void RIGHT (void)\n+{\n+\tChangeMe;\n+}\n+EOF_TEST\ndiff --git a/t/t4018/css-attribute-value-selector b/t/t4018/css-attribute-value-selector\ndeleted file mode 100644\nindex 918256b20c..0000000000\n--- a/t/t4018/css-attribute-value-selector\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-[class*=\"RIGHT\"] {\n-    background : #000;\n-    border : 10px ChangeMe #C6C6C6;\n-}\ndiff --git a/t/t4018/css-attribute-value-selector.ctx b/t/t4018/css-attribute-value-selector.ctx\ndeleted file mode 100644\nindex 7f8956251c..0000000000\n--- a/t/t4018/css-attribute-value-selector.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-[class*=\"RIGHT\"] {\ndiff --git a/t/t4018/css-block-level-@-statements b/t/t4018/css-block-level-@-statements\ndeleted file mode 100644\nindex d6755f2f3d..0000000000\n--- a/t/t4018/css-block-level-@-statements\n+++ /dev/null\n@@ -1,10 +0,0 @@\n-@keyframes RIGHT {\n-    from {\n-        background : #000;\n-        border : 10px ChangeMe #C6C6C6;\n-    }\n-    to {\n-        background : #fff;\n-        border : 10px solid #C6C6C6;\n-    }\n-}\ndiff --git a/t/t4018/css-block-level-@-statements.ctx b/t/t4018/css-block-level-@-statements.ctx\ndeleted file mode 100644\nindex 7f5e90468c..0000000000\n--- a/t/t4018/css-block-level-@-statements.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-@keyframes RIGHT {\ndiff --git a/t/t4018/css-brace-in-col-1 b/t/t4018/css-brace-in-col-1\ndeleted file mode 100644\nindex 7831577506..0000000000\n--- a/t/t4018/css-brace-in-col-1\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-RIGHT label.control-label\n-{\n-    margin-top: 10px!important;\n-    border : 10px ChangeMe #C6C6C6;\n-}\ndiff --git a/t/t4018/css-brace-in-col-1.ctx b/t/t4018/css-brace-in-col-1.ctx\ndeleted file mode 100644\nindex 91a9105c6a..0000000000\n--- a/t/t4018/css-brace-in-col-1.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT label.control-label\ndiff --git a/t/t4018/css-class-selector b/t/t4018/css-class-selector\ndeleted file mode 100644\nindex f790a0062f..0000000000\n--- a/t/t4018/css-class-selector\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-.RIGHT {\n-    background : #000;\n-    border : 10px ChangeMe #C6C6C6;\n-}\ndiff --git a/t/t4018/css-class-selector.ctx b/t/t4018/css-class-selector.ctx\ndeleted file mode 100644\nindex ac7367d7f4..0000000000\n--- a/t/t4018/css-class-selector.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-.RIGHT {\ndiff --git a/t/t4018/css-colon-eol b/t/t4018/css-colon-eol\ndeleted file mode 100644\nindex 5a30553d29..0000000000\n--- a/t/t4018/css-colon-eol\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-RIGHT h1 {\n-color:\n-ChangeMe;\n-}\ndiff --git a/t/t4018/css-colon-eol.ctx b/t/t4018/css-colon-eol.ctx\ndeleted file mode 100644\nindex b68493b9b0..0000000000\n--- a/t/t4018/css-colon-eol.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT h1 {\ndiff --git a/t/t4018/css-colon-selector b/t/t4018/css-colon-selector\ndeleted file mode 100644\nindex c6d71fb42d..0000000000\n--- a/t/t4018/css-colon-selector\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-RIGHT a:hover {\n-    margin-top:\n-    10px!important;\n-    border : 10px ChangeMe #C6C6C6;\n-}\ndiff --git a/t/t4018/css-colon-selector.ctx b/t/t4018/css-colon-selector.ctx\ndeleted file mode 100644\nindex 00b1a5aefe..0000000000\n--- a/t/t4018/css-colon-selector.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT a:hover {\ndiff --git a/t/t4018/css-common b/t/t4018/css-common\ndeleted file mode 100644\nindex 84ed754b33..0000000000\n--- a/t/t4018/css-common\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-RIGHT label.control-label {\n-    margin-top: 10px!important;\n-    border : 10px ChangeMe #C6C6C6;\n-}\ndiff --git a/t/t4018/css-common.ctx b/t/t4018/css-common.ctx\ndeleted file mode 100644\nindex 43686b4081..0000000000\n--- a/t/t4018/css-common.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT label.control-label {\ndiff --git a/t/t4018/css-id-selector b/t/t4018/css-id-selector\ndeleted file mode 100644\nindex 17c5111052..0000000000\n--- a/t/t4018/css-id-selector\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-#RIGHT {\n-    background : #000;\n-    border : 10px ChangeMe #C6C6C6;\n-}\ndiff --git a/t/t4018/css-id-selector.ctx b/t/t4018/css-id-selector.ctx\ndeleted file mode 100644\nindex ce19f6d8dc..0000000000\n--- a/t/t4018/css-id-selector.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-#RIGHT {\ndiff --git a/t/t4018/css-long-selector-list b/t/t4018/css-long-selector-list\ndeleted file mode 100644\nindex 7ccd25d9ed..0000000000\n--- a/t/t4018/css-long-selector-list\n+++ /dev/null\n@@ -1,6 +0,0 @@\n-p.header,\n-label.control-label,\n-div ul#RIGHT {\n-    margin-top: 10px!important;\n-    border : 10px ChangeMe #C6C6C6;\n-}\ndiff --git a/t/t4018/css-long-selector-list.ctx b/t/t4018/css-long-selector-list.ctx\ndeleted file mode 100644\nindex bc8d0fb62c..0000000000\n--- a/t/t4018/css-long-selector-list.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-div ul#RIGHT {\ndiff --git a/t/t4018/css-prop-sans-indent b/t/t4018/css-prop-sans-indent\ndeleted file mode 100644\nindex a9e3c86b3c..0000000000\n--- a/t/t4018/css-prop-sans-indent\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-RIGHT, label.control-label {\n-margin-top: 10px!important;\n-padding: 0;\n-border : 10px ChangeMe #C6C6C6;\n-}\ndiff --git a/t/t4018/css-prop-sans-indent.ctx b/t/t4018/css-prop-sans-indent.ctx\ndeleted file mode 100644\nindex cc880b2f44..0000000000\n--- a/t/t4018/css-prop-sans-indent.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT, label.control-label {\ndiff --git a/t/t4018/css-root-selector b/t/t4018/css-root-selector\ndeleted file mode 100644\nindex 22b958e369..0000000000\n--- a/t/t4018/css-root-selector\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-:RIGHT {\n-    background : #000;\n-    border : 10px ChangeMe #C6C6C6;\n-}\ndiff --git a/t/t4018/css-root-selector.ctx b/t/t4018/css-root-selector.ctx\ndeleted file mode 100644\nindex 3010cded2a..0000000000\n--- a/t/t4018/css-root-selector.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-:RIGHT {\ndiff --git a/t/t4018/css-short-selector-list b/t/t4018/css-short-selector-list\ndeleted file mode 100644\nindex 6a0bdee336..0000000000\n--- a/t/t4018/css-short-selector-list\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-label.control, div ul#RIGHT {\n-    margin-top: 10px!important;\n-    border : 10px ChangeMe #C6C6C6;\n-}\ndiff --git a/t/t4018/css-short-selector-list.ctx b/t/t4018/css-short-selector-list.ctx\ndeleted file mode 100644\nindex 9e5d87d126..0000000000\n--- a/t/t4018/css-short-selector-list.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-label.control, div ul#RIGHT {\ndiff --git a/t/t4018/css-trailing-space b/t/t4018/css-trailing-space\ndeleted file mode 100644\nindex 32b5606c70..0000000000\n--- a/t/t4018/css-trailing-space\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-RIGHT label.control-label {\n-    margin:10px;   \n-    padding:10px;\n-    border : 10px ChangeMe #C6C6C6;\n-}\ndiff --git a/t/t4018/css-trailing-space.ctx b/t/t4018/css-trailing-space.ctx\ndeleted file mode 100644\nindex 43686b4081..0000000000\n--- a/t/t4018/css-trailing-space.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT label.control-label {\ndiff --git a/t/t4018/css.sh b/t/t4018/css.sh\nnew file mode 100755\nindex 0000000000..106a3de242\n--- /dev/null\n+++ b/t/t4018/css.sh\n@@ -0,0 +1,146 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'css: attribute value selector' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+[class*=\"RIGHT\"] {\n+EOF_HUNK\n+[class*=\"RIGHT\"] {\n+    background : #000;\n+    border : 10px ChangeMe #C6C6C6;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'css: block level @ statements' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+@keyframes RIGHT {\n+EOF_HUNK\n+@keyframes RIGHT {\n+    from {\n+        background : #000;\n+        border : 10px ChangeMe #C6C6C6;\n+    }\n+    to {\n+        background : #fff;\n+        border : 10px solid #C6C6C6;\n+    }\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'css: brace in col 1' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT label.control-label\n+EOF_HUNK\n+RIGHT label.control-label\n+{\n+    margin-top: 10px!important;\n+    border : 10px ChangeMe #C6C6C6;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'css: class selector' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+.RIGHT {\n+EOF_HUNK\n+.RIGHT {\n+    background : #000;\n+    border : 10px ChangeMe #C6C6C6;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'css: colon eol' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT h1 {\n+EOF_HUNK\n+RIGHT h1 {\n+color:\n+ChangeMe;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'css: colon selector' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT a:hover {\n+EOF_HUNK\n+RIGHT a:hover {\n+    margin-top:\n+    10px!important;\n+    border : 10px ChangeMe #C6C6C6;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'css: common' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT label.control-label {\n+EOF_HUNK\n+RIGHT label.control-label {\n+    margin-top: 10px!important;\n+    border : 10px ChangeMe #C6C6C6;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'css: id selector' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+#RIGHT {\n+EOF_HUNK\n+#RIGHT {\n+    background : #000;\n+    border : 10px ChangeMe #C6C6C6;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'css: long selector list' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+div ul#RIGHT {\n+EOF_HUNK\n+p.header,\n+label.control-label,\n+div ul#RIGHT {\n+    margin-top: 10px!important;\n+    border : 10px ChangeMe #C6C6C6;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'css: prop sans indent' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT, label.control-label {\n+EOF_HUNK\n+RIGHT, label.control-label {\n+margin-top: 10px!important;\n+padding: 0;\n+border : 10px ChangeMe #C6C6C6;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'css: root selector' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+:RIGHT {\n+EOF_HUNK\n+:RIGHT {\n+    background : #000;\n+    border : 10px ChangeMe #C6C6C6;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'css: short selector list' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+label.control, div ul#RIGHT {\n+EOF_HUNK\n+label.control, div ul#RIGHT {\n+    margin-top: 10px!important;\n+    border : 10px ChangeMe #C6C6C6;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'css: trailing space' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT label.control-label {\n+EOF_HUNK\n+RIGHT label.control-label {\n+    margin:10px;   \n+    padding:10px;\n+    border : 10px ChangeMe #C6C6C6;\n+}\n+EOF_TEST\ndiff --git a/t/t4018/custom1-pattern.ctx b/t/t4018/custom1-pattern.ctx\ndeleted file mode 100644\nindex d1609cc9a6..0000000000\n--- a/t/t4018/custom1-pattern.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-int special, RIGHT;\ndiff --git a/t/t4018/custom1-pattern b/t/t4018/custom1.sh\nold mode 100644\nnew mode 100755\nsimilarity index 71%\nrename from t/t4018/custom1-pattern\nrename to t/t4018/custom1.sh\nindex e8fd59f884..f8bbccadb4\n--- a/t/t4018/custom1-pattern\n+++ b/t/t4018/custom1.sh\n@@ -1,3 +1,12 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'custom1: pattern' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+int special, RIGHT;\n+EOF_HUNK\n public class Beer\n {\n \tint special, RIGHT;\n@@ -15,3 +24,4 @@ public class Beer\n \t\t\t+ \"99 bottles of beer on the wall.\\n\");\n \t}\n }\n+EOF_TEST\ndiff --git a/t/t4018/custom2-match-to-end-of-line b/t/t4018/custom2-match-to-end-of-line\ndeleted file mode 100644\nindex f88ac318b7..0000000000\n--- a/t/t4018/custom2-match-to-end-of-line\n+++ /dev/null\n@@ -1,8 +0,0 @@\n-public class RIGHT_Beer\n-{\n-\tint special;\n-\tpublic static void main(String args[])\n-\t{\n-\t\tSystem.out.print(\"ChangeMe\");\n-\t}\n-}\ndiff --git a/t/t4018/custom2-match-to-end-of-line.ctx b/t/t4018/custom2-match-to-end-of-line.ctx\ndeleted file mode 100644\nindex 8294c6e49b..0000000000\n--- a/t/t4018/custom2-match-to-end-of-line.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT_Beer\ndiff --git a/t/t4018/custom2.sh b/t/t4018/custom2.sh\nnew file mode 100755\nindex 0000000000..c68421f788\n--- /dev/null\n+++ b/t/t4018/custom2.sh\n@@ -0,0 +1,18 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'custom2: match to end of line' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT_Beer\n+EOF_HUNK\n+public class RIGHT_Beer\n+{\n+\tint special;\n+\tpublic static void main(String args[])\n+\t{\n+\t\tSystem.out.print(\"ChangeMe\");\n+\t}\n+}\n+EOF_TEST\ndiff --git a/t/t4018/custom3-alternation-in-pattern.ctx b/t/t4018/custom3-alternation-in-pattern.ctx\ndeleted file mode 100644\nindex 2125474b68..0000000000\n--- a/t/t4018/custom3-alternation-in-pattern.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-public static void main(String RIGHT[])\ndiff --git a/t/t4018/custom3-alternation-in-pattern b/t/t4018/custom3.sh\nold mode 100644\nnew mode 100755\nsimilarity index 66%\nrename from t/t4018/custom3-alternation-in-pattern\nrename to t/t4018/custom3.sh\nindex 5f3769c64f..07c5c134ff\n--- a/t/t4018/custom3-alternation-in-pattern\n+++ b/t/t4018/custom3.sh\n@@ -1,3 +1,12 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'custom3: alternation in pattern' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+public static void main(String RIGHT[])\n+EOF_HUNK\n public class Beer\n {\n \tint special;\n@@ -15,3 +24,4 @@ public class Beer\n \t\t\t+ \"99 bottles of beer on the wall.\\n\");\n \t}\n }\n+EOF_TEST\ndiff --git a/t/t4018/dts-labels b/t/t4018/dts-labels\ndeleted file mode 100644\nindex b21ef8737b..0000000000\n--- a/t/t4018/dts-labels\n+++ /dev/null\n@@ -1,9 +0,0 @@\n-/ {\n-\tlabel_1: node1@ff00 {\n-\t\tlabel2: RIGHT {\n-\t\t\tvendor,some-property;\n-\n-\t\t\tChangeMe = <0x45-30>;\n-\t\t};\n-\t};\n-};\ndiff --git a/t/t4018/dts-labels.ctx b/t/t4018/dts-labels.ctx\ndeleted file mode 100644\nindex 48d9373cab..0000000000\n--- a/t/t4018/dts-labels.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-label2: RIGHT {\ndiff --git a/t/t4018/dts-node-unitless b/t/t4018/dts-node-unitless\ndeleted file mode 100644\nindex c5287d9141..0000000000\n--- a/t/t4018/dts-node-unitless\n+++ /dev/null\n@@ -1,8 +0,0 @@\n-/ {\n-\tlabel_1: node1 {\n-\t\tRIGHT {\n-\t\t\tprop-array = <1>, <4>;\n-\t\t\tChangeMe = <0xffeedd00>;\n-\t\t};\n-\t};\n-};\ndiff --git a/t/t4018/dts-node-unitless.ctx b/t/t4018/dts-node-unitless.ctx\ndeleted file mode 100644\nindex 82c8683fa1..0000000000\n--- a/t/t4018/dts-node-unitless.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT {\ndiff --git a/t/t4018/dts-nodes b/t/t4018/dts-nodes\ndeleted file mode 100644\nindex 5a4334bb16..0000000000\n--- a/t/t4018/dts-nodes\n+++ /dev/null\n@@ -1,8 +0,0 @@\n-/ {\n-\tlabel_1: node1@ff00 {\n-\t\tRIGHT@deadf00,4000 {\n-\t\t\t#size-cells = <1>;\n-\t\t\tChangeMe = <0xffeedd00>;\n-\t\t};\n-\t};\n-};\ndiff --git a/t/t4018/dts-nodes-boolean-prop b/t/t4018/dts-nodes-boolean-prop\ndeleted file mode 100644\nindex afc6b5b404..0000000000\n--- a/t/t4018/dts-nodes-boolean-prop\n+++ /dev/null\n@@ -1,9 +0,0 @@\n-/ {\n-\tlabel_1: node1@ff00 {\n-\t\tRIGHT@deadf00,4000 {\n-\t\t\tboolean-prop1;\n-\n-\t\t\tChangeMe;\n-\t\t};\n-\t};\n-};\ndiff --git a/t/t4018/dts-nodes-boolean-prop.ctx b/t/t4018/dts-nodes-boolean-prop.ctx\ndeleted file mode 100644\nindex 3a0232d55d..0000000000\n--- a/t/t4018/dts-nodes-boolean-prop.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT@deadf00,4000 {\ndiff --git a/t/t4018/dts-nodes-comment1 b/t/t4018/dts-nodes-comment1\ndeleted file mode 100644\nindex 559dfce9b3..0000000000\n--- a/t/t4018/dts-nodes-comment1\n+++ /dev/null\n@@ -1,8 +0,0 @@\n-/ {\n-\tlabel_1: node1@ff00 {\n-\t\tRIGHT@deadf00,4000 /* &a comment */ {\n-\t\t\t#size-cells = <1>;\n-\t\t\tChangeMe = <0xffeedd00>;\n-\t\t};\n-\t};\n-};\ndiff --git a/t/t4018/dts-nodes-comment1.ctx b/t/t4018/dts-nodes-comment1.ctx\ndeleted file mode 100644\nindex ec364600b1..0000000000\n--- a/t/t4018/dts-nodes-comment1.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT@deadf00,4000 /* &a comment */ {\ndiff --git a/t/t4018/dts-nodes-comment2 b/t/t4018/dts-nodes-comment2\ndeleted file mode 100644\nindex 27e9718b31..0000000000\n--- a/t/t4018/dts-nodes-comment2\n+++ /dev/null\n@@ -1,8 +0,0 @@\n-/ {\n-\tlabel_1: node1@ff00 {\n-\t\tRIGHT@deadf00,4000 { /* a trailing comment */ \n-\t\t\t#size-cells = <1>;\n-\t\t\tChangeMe = <0xffeedd00>;\n-\t\t};\n-\t};\n-};\ndiff --git a/t/t4018/dts-nodes-comment2.ctx b/t/t4018/dts-nodes-comment2.ctx\ndeleted file mode 100644\nindex 75f0d75258..0000000000\n--- a/t/t4018/dts-nodes-comment2.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT@deadf00,4000 { /* a trailing comment */\ndiff --git a/t/t4018/dts-nodes-multiline-prop b/t/t4018/dts-nodes-multiline-prop\ndeleted file mode 100644\nindex 072d58b69d..0000000000\n--- a/t/t4018/dts-nodes-multiline-prop\n+++ /dev/null\n@@ -1,13 +0,0 @@\n-/ {\n-\tlabel_1: node1@ff00 {\n-\t\tRIGHT@deadf00,4000 {\n-\t\t\tmultilineprop = <3>,\n-\t\t\t\t\t<4>,\n-\t\t\t\t\t<5>,\n-\t\t\t\t\t<6>,\n-\t\t\t\t\t<7>;\n-\n-\t\t\tChangeMe = <0xffeedd00>;\n-\t\t};\n-\t};\n-};\ndiff --git a/t/t4018/dts-nodes-multiline-prop.ctx b/t/t4018/dts-nodes-multiline-prop.ctx\ndeleted file mode 100644\nindex 3a0232d55d..0000000000\n--- a/t/t4018/dts-nodes-multiline-prop.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT@deadf00,4000 {\ndiff --git a/t/t4018/dts-nodes.ctx b/t/t4018/dts-nodes.ctx\ndeleted file mode 100644\nindex 3a0232d55d..0000000000\n--- a/t/t4018/dts-nodes.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT@deadf00,4000 {\ndiff --git a/t/t4018/dts-reference b/t/t4018/dts-reference\ndeleted file mode 100644\nindex 8f0c87d863..0000000000\n--- a/t/t4018/dts-reference\n+++ /dev/null\n@@ -1,9 +0,0 @@\n-&label_1 {\n-\tTEST = <455>;\n-};\n-\n-&RIGHT {\n-\tvendor,some-property;\n-\n-\tChangeMe = <0x45-30>;\n-};\ndiff --git a/t/t4018/dts-reference.ctx b/t/t4018/dts-reference.ctx\ndeleted file mode 100644\nindex c1e13409ee..0000000000\n--- a/t/t4018/dts-reference.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-&RIGHT {\ndiff --git a/t/t4018/dts-root b/t/t4018/dts-root\ndeleted file mode 100644\nindex 4353b8220c..0000000000\n--- a/t/t4018/dts-root\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-/ { RIGHT /* Technically just supposed to be a slash and brace */\n-\t#size-cells = <1>;\n-\n-\tChangeMe = <0xffeedd00>;\n-};\ndiff --git a/t/t4018/dts-root-comment b/t/t4018/dts-root-comment\ndeleted file mode 100644\nindex 333a625c70..0000000000\n--- a/t/t4018/dts-root-comment\n+++ /dev/null\n@@ -1,8 +0,0 @@\n-/ { RIGHT /* Technically just supposed to be a slash and brace */\n-\t#size-cells = <1>;\n-\n-\t/* This comment should be ignored */\n-\n-\tsome-property = <40+2>;\n-\tChangeMe = <0xffeedd00>;\n-};\ndiff --git a/t/t4018/dts-root-comment.ctx b/t/t4018/dts-root-comment.ctx\ndeleted file mode 100644\nindex 656053dd42..0000000000\n--- a/t/t4018/dts-root-comment.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-/ { RIGHT /* Technically just supposed to be a slash and brace */\ndiff --git a/t/t4018/dts-root.ctx b/t/t4018/dts-root.ctx\ndeleted file mode 100644\nindex 656053dd42..0000000000\n--- a/t/t4018/dts-root.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-/ { RIGHT /* Technically just supposed to be a slash and brace */\ndiff --git a/t/t4018/dts.sh b/t/t4018/dts.sh\nnew file mode 100755\nindex 0000000000..304c131d86\n--- /dev/null\n+++ b/t/t4018/dts.sh\n@@ -0,0 +1,149 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'dts: labels' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+label2: RIGHT {\n+EOF_HUNK\n+/ {\n+\tlabel_1: node1@ff00 {\n+\t\tlabel2: RIGHT {\n+\t\t\tvendor,some-property;\n+\n+\t\t\tChangeMe = <0x45-30>;\n+\t\t};\n+\t};\n+};\n+EOF_TEST\n+\n+test_diff_funcname 'dts: node unitless' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT {\n+EOF_HUNK\n+/ {\n+\tlabel_1: node1 {\n+\t\tRIGHT {\n+\t\t\tprop-array = <1>, <4>;\n+\t\t\tChangeMe = <0xffeedd00>;\n+\t\t};\n+\t};\n+};\n+EOF_TEST\n+\n+test_diff_funcname 'dts: nodes' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT@deadf00,4000 {\n+EOF_HUNK\n+/ {\n+\tlabel_1: node1@ff00 {\n+\t\tRIGHT@deadf00,4000 {\n+\t\t\t#size-cells = <1>;\n+\t\t\tChangeMe = <0xffeedd00>;\n+\t\t};\n+\t};\n+};\n+EOF_TEST\n+\n+test_diff_funcname 'dts: nodes boolean prop' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT@deadf00,4000 {\n+EOF_HUNK\n+/ {\n+\tlabel_1: node1@ff00 {\n+\t\tRIGHT@deadf00,4000 {\n+\t\t\tboolean-prop1;\n+\n+\t\t\tChangeMe;\n+\t\t};\n+\t};\n+};\n+EOF_TEST\n+\n+test_diff_funcname 'dts: nodes comment1' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT@deadf00,4000 /* &a comment */ {\n+EOF_HUNK\n+/ {\n+\tlabel_1: node1@ff00 {\n+\t\tRIGHT@deadf00,4000 /* &a comment */ {\n+\t\t\t#size-cells = <1>;\n+\t\t\tChangeMe = <0xffeedd00>;\n+\t\t};\n+\t};\n+};\n+EOF_TEST\n+\n+test_diff_funcname 'dts: nodes comment2' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT@deadf00,4000 { /* a trailing comment */\n+EOF_HUNK\n+/ {\n+\tlabel_1: node1@ff00 {\n+\t\tRIGHT@deadf00,4000 { /* a trailing comment */ \n+\t\t\t#size-cells = <1>;\n+\t\t\tChangeMe = <0xffeedd00>;\n+\t\t};\n+\t};\n+};\n+EOF_TEST\n+\n+test_diff_funcname 'dts: nodes multiline prop' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT@deadf00,4000 {\n+EOF_HUNK\n+/ {\n+\tlabel_1: node1@ff00 {\n+\t\tRIGHT@deadf00,4000 {\n+\t\t\tmultilineprop = <3>,\n+\t\t\t\t\t<4>,\n+\t\t\t\t\t<5>,\n+\t\t\t\t\t<6>,\n+\t\t\t\t\t<7>;\n+\n+\t\t\tChangeMe = <0xffeedd00>;\n+\t\t};\n+\t};\n+};\n+EOF_TEST\n+\n+test_diff_funcname 'dts: reference' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+&RIGHT {\n+EOF_HUNK\n+&label_1 {\n+\tTEST = <455>;\n+};\n+\n+&RIGHT {\n+\tvendor,some-property;\n+\n+\tChangeMe = <0x45-30>;\n+};\n+EOF_TEST\n+\n+test_diff_funcname 'dts: root' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+/ { RIGHT /* Technically just supposed to be a slash and brace */\n+EOF_HUNK\n+/ { RIGHT /* Technically just supposed to be a slash and brace */\n+\t#size-cells = <1>;\n+\n+\tChangeMe = <0xffeedd00>;\n+};\n+EOF_TEST\n+\n+test_diff_funcname 'dts: root comment' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+/ { RIGHT /* Technically just supposed to be a slash and brace */\n+EOF_HUNK\n+/ { RIGHT /* Technically just supposed to be a slash and brace */\n+\t#size-cells = <1>;\n+\n+\t/* This comment should be ignored */\n+\n+\tsome-property = <40+2>;\n+\tChangeMe = <0xffeedd00>;\n+};\n+EOF_TEST\ndiff --git a/t/t4018/elixir-do-not-pick-end b/t/t4018/elixir-do-not-pick-end\ndeleted file mode 100644\nindex fae08ba7e8..0000000000\n--- a/t/t4018/elixir-do-not-pick-end\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-defmodule RIGHT do\n-end\n-#\n-#\n-# ChangeMe; do not pick up 'end' line\ndiff --git a/t/t4018/elixir-do-not-pick-end.ctx b/t/t4018/elixir-do-not-pick-end.ctx\ndeleted file mode 100644\nindex 8f28a7a689..0000000000\n--- a/t/t4018/elixir-do-not-pick-end.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-defmodule RIGHT do\ndiff --git a/t/t4018/elixir-ex-unit-test b/t/t4018/elixir-ex-unit-test\ndeleted file mode 100644\nindex 0560a2b697..0000000000\n--- a/t/t4018/elixir-ex-unit-test\n+++ /dev/null\n@@ -1,6 +0,0 @@\n-defmodule Test do\n-  test \"RIGHT\" do\n-    assert true == true\n-    assert ChangeMe\n-  end\n-end\ndiff --git a/t/t4018/elixir-ex-unit-test.ctx b/t/t4018/elixir-ex-unit-test.ctx\ndeleted file mode 100644\nindex a55e3de2cc..0000000000\n--- a/t/t4018/elixir-ex-unit-test.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-test \"RIGHT\" do\ndiff --git a/t/t4018/elixir-function b/t/t4018/elixir-function\ndeleted file mode 100644\nindex d452f495a7..0000000000\n--- a/t/t4018/elixir-function\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-def function(RIGHT, arg) do\n-  # comment\n-  # comment\n-  ChangeMe\n-end\ndiff --git a/t/t4018/elixir-function.ctx b/t/t4018/elixir-function.ctx\ndeleted file mode 100644\nindex 62aee9c8b1..0000000000\n--- a/t/t4018/elixir-function.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-def function(RIGHT, arg) do\ndiff --git a/t/t4018/elixir-macro b/t/t4018/elixir-macro\ndeleted file mode 100644\nindex 4f925e9ad4..0000000000\n--- a/t/t4018/elixir-macro\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-defmacro foo(RIGHT) do\n-  # Code\n-  # Code\n-  ChangeMe\n-end\ndiff --git a/t/t4018/elixir-macro.ctx b/t/t4018/elixir-macro.ctx\ndeleted file mode 100644\nindex fc1d3b85e8..0000000000\n--- a/t/t4018/elixir-macro.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-defmacro foo(RIGHT) do\ndiff --git a/t/t4018/elixir-module b/t/t4018/elixir-module\ndeleted file mode 100644\nindex 91a4e7aa20..0000000000\n--- a/t/t4018/elixir-module\n+++ /dev/null\n@@ -1,9 +0,0 @@\n-defmodule RIGHT do\n-  @moduledoc \"\"\"\n-  Foo bar\n-  \"\"\"\n-\n-  def ChangeMe(a) where is_map(a) do\n-    a\n-  end\n-end\ndiff --git a/t/t4018/elixir-module-func b/t/t4018/elixir-module-func\ndeleted file mode 100644\nindex c9910d0675..0000000000\n--- a/t/t4018/elixir-module-func\n+++ /dev/null\n@@ -1,8 +0,0 @@\n-defmodule Foo do\n-  def fun(RIGHT) do\n-     # Code\n-     # Code\n-     # Code\n-     ChangeMe\n-  end\n-end\ndiff --git a/t/t4018/elixir-module-func.ctx b/t/t4018/elixir-module-func.ctx\ndeleted file mode 100644\nindex 8239214386..0000000000\n--- a/t/t4018/elixir-module-func.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-def fun(RIGHT) do\ndiff --git a/t/t4018/elixir-module.ctx b/t/t4018/elixir-module.ctx\ndeleted file mode 100644\nindex 8f28a7a689..0000000000\n--- a/t/t4018/elixir-module.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-defmodule RIGHT do\ndiff --git a/t/t4018/elixir-nested-module b/t/t4018/elixir-nested-module\ndeleted file mode 100644\nindex 771ebc5c42..0000000000\n--- a/t/t4018/elixir-nested-module\n+++ /dev/null\n@@ -1,9 +0,0 @@\n-defmodule MyApp.RIGHT do\n-  @moduledoc \"\"\"\n-  Foo bar\n-  \"\"\"\n-\n-  def ChangeMe(a) where is_map(a) do\n-    a\n-  end\n-end\ndiff --git a/t/t4018/elixir-nested-module.ctx b/t/t4018/elixir-nested-module.ctx\ndeleted file mode 100644\nindex 3ffbdd18b1..0000000000\n--- a/t/t4018/elixir-nested-module.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-defmodule MyApp.RIGHT do\ndiff --git a/t/t4018/elixir-private-function b/t/t4018/elixir-private-function\ndeleted file mode 100644\nindex 1aabe33b7a..0000000000\n--- a/t/t4018/elixir-private-function\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-defp function(RIGHT, arg) do\n-  # comment\n-  # comment\n-  ChangeMe\n-end\ndiff --git a/t/t4018/elixir-private-function.ctx b/t/t4018/elixir-private-function.ctx\ndeleted file mode 100644\nindex 1c4eba44f7..0000000000\n--- a/t/t4018/elixir-private-function.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-defp function(RIGHT, arg) do\ndiff --git a/t/t4018/elixir-protocol b/t/t4018/elixir-protocol\ndeleted file mode 100644\nindex 7d9173691e..0000000000\n--- a/t/t4018/elixir-protocol\n+++ /dev/null\n@@ -1,6 +0,0 @@\n-defprotocol RIGHT do\n-  @doc \"\"\"\n-  Calculates the size (and not the length!) of a data structure\n-  \"\"\"\n-  def size(data, ChangeMe)\n-end\ndiff --git a/t/t4018/elixir-protocol-implementation b/t/t4018/elixir-protocol-implementation\ndeleted file mode 100644\nindex f9234bbfc4..0000000000\n--- a/t/t4018/elixir-protocol-implementation\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-defimpl RIGHT do\n-  # Docs\n-  # Docs\n-  def foo(ChangeMe), do: :ok\n-end\ndiff --git a/t/t4018/elixir-protocol-implementation.ctx b/t/t4018/elixir-protocol-implementation.ctx\ndeleted file mode 100644\nindex efb758aea6..0000000000\n--- a/t/t4018/elixir-protocol-implementation.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-defimpl RIGHT do\ndiff --git a/t/t4018/elixir-protocol.ctx b/t/t4018/elixir-protocol.ctx\ndeleted file mode 100644\nindex d0204e9f14..0000000000\n--- a/t/t4018/elixir-protocol.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-defprotocol RIGHT do\ndiff --git a/t/t4018/elixir.sh b/t/t4018/elixir.sh\nnew file mode 100755\nindex 0000000000..6b09df9520\n--- /dev/null\n+++ b/t/t4018/elixir.sh\n@@ -0,0 +1,127 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'elixir: do not pick end' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+defmodule RIGHT do\n+EOF_HUNK\n+defmodule RIGHT do\n+end\n+#\n+#\n+# ChangeMe; do not pick up 'end' line\n+EOF_TEST\n+\n+test_diff_funcname 'elixir: ex unit test' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+test \"RIGHT\" do\n+EOF_HUNK\n+defmodule Test do\n+  test \"RIGHT\" do\n+    assert true == true\n+    assert ChangeMe\n+  end\n+end\n+EOF_TEST\n+\n+test_diff_funcname 'elixir: function' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+def function(RIGHT, arg) do\n+EOF_HUNK\n+def function(RIGHT, arg) do\n+  # comment\n+  # comment\n+  ChangeMe\n+end\n+EOF_TEST\n+\n+test_diff_funcname 'elixir: macro' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+defmacro foo(RIGHT) do\n+EOF_HUNK\n+defmacro foo(RIGHT) do\n+  # Code\n+  # Code\n+  ChangeMe\n+end\n+EOF_TEST\n+\n+test_diff_funcname 'elixir: module' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+defmodule RIGHT do\n+EOF_HUNK\n+defmodule RIGHT do\n+  @moduledoc \"\"\"\n+  Foo bar\n+  \"\"\"\n+\n+  def ChangeMe(a) where is_map(a) do\n+    a\n+  end\n+end\n+EOF_TEST\n+\n+test_diff_funcname 'elixir: module func' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+def fun(RIGHT) do\n+EOF_HUNK\n+defmodule Foo do\n+  def fun(RIGHT) do\n+     # Code\n+     # Code\n+     # Code\n+     ChangeMe\n+  end\n+end\n+EOF_TEST\n+\n+test_diff_funcname 'elixir: nested module' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+defmodule MyApp.RIGHT do\n+EOF_HUNK\n+defmodule MyApp.RIGHT do\n+  @moduledoc \"\"\"\n+  Foo bar\n+  \"\"\"\n+\n+  def ChangeMe(a) where is_map(a) do\n+    a\n+  end\n+end\n+EOF_TEST\n+\n+test_diff_funcname 'elixir: private function' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+defp function(RIGHT, arg) do\n+EOF_HUNK\n+defp function(RIGHT, arg) do\n+  # comment\n+  # comment\n+  ChangeMe\n+end\n+EOF_TEST\n+\n+test_diff_funcname 'elixir: protocol' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+defprotocol RIGHT do\n+EOF_HUNK\n+defprotocol RIGHT do\n+  @doc \"\"\"\n+  Calculates the size (and not the length!) of a data structure\n+  \"\"\"\n+  def size(data, ChangeMe)\n+end\n+EOF_TEST\n+\n+test_diff_funcname 'elixir: protocol implementation' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+defimpl RIGHT do\n+EOF_HUNK\n+defimpl RIGHT do\n+  # Docs\n+  # Docs\n+  def foo(ChangeMe), do: :ok\n+end\n+EOF_TEST\ndiff --git a/t/t4018/fortran-block-data b/t/t4018/fortran-block-data\ndeleted file mode 100644\nindex 63d4e21d0a..0000000000\n--- a/t/t4018/fortran-block-data\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-       BLOCK DATA RIGHT\n-       \n-       COMMON /B/ C, ChangeMe\n-       DATA C, ChangeMe  / 2.0, 6.0 / \n-       END \ndiff --git a/t/t4018/fortran-block-data.ctx b/t/t4018/fortran-block-data.ctx\ndeleted file mode 100644\nindex c3db084ccc..0000000000\n--- a/t/t4018/fortran-block-data.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-BLOCK DATA RIGHT\ndiff --git a/t/t4018/fortran-comment b/t/t4018/fortran-comment\ndeleted file mode 100644\nindex 7b10d17658..0000000000\n--- a/t/t4018/fortran-comment\n+++ /dev/null\n@@ -1,13 +0,0 @@\n-      module a\n-\n-      contains\n-\n-      ! subroutine wrong\n-      subroutine RIGHT\n-      ! subroutine wrong\n-\n-      real ChangeMe\n-\n-      end subroutine RIGHT\n-\n-      end module a\ndiff --git a/t/t4018/fortran-comment-keyword b/t/t4018/fortran-comment-keyword\ndeleted file mode 100644\nindex e9206a5379..0000000000\n--- a/t/t4018/fortran-comment-keyword\n+++ /dev/null\n@@ -1,14 +0,0 @@\n-      module a\n-\n-      contains\n-\n-      subroutine RIGHT (funcA, funcB)\n-\n-      real funcA  ! grid function a\n-      real funcB  ! grid function b\n-\n-      real ChangeMe\n-\n-      end subroutine RIGHT\n-\n-      end module a\ndiff --git a/t/t4018/fortran-comment-keyword.ctx b/t/t4018/fortran-comment-keyword.ctx\ndeleted file mode 100644\nindex 0b9220b355..0000000000\n--- a/t/t4018/fortran-comment-keyword.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-subroutine RIGHT (funcA, funcB)\ndiff --git a/t/t4018/fortran-comment-legacy b/t/t4018/fortran-comment-legacy\ndeleted file mode 100644\nindex 53cd062c1e..0000000000\n--- a/t/t4018/fortran-comment-legacy\n+++ /dev/null\n@@ -1,13 +0,0 @@\n-      module a\n-\n-      contains\n-\n-C subroutine wrong\n-      subroutine RIGHT\n-C subroutine wrong\n-\n-      real ChangeMe\n-\n-      end subroutine RIGHT\n-\n-      end module a\ndiff --git a/t/t4018/fortran-comment-legacy-star b/t/t4018/fortran-comment-legacy-star\ndeleted file mode 100644\nindex 2cbcdc3d8a..0000000000\n--- a/t/t4018/fortran-comment-legacy-star\n+++ /dev/null\n@@ -1,13 +0,0 @@\n-      module a\n-\n-      contains\n-\n-* subroutine wrong\n-      subroutine RIGHT\n-* subroutine wrong\n-\n-      real ChangeMe\n-\n-      end subroutine RIGHT\n-\n-      end module a\ndiff --git a/t/t4018/fortran-comment-legacy-star.ctx b/t/t4018/fortran-comment-legacy-star.ctx\ndeleted file mode 100644\nindex 6a34203f80..0000000000\n--- a/t/t4018/fortran-comment-legacy-star.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-subroutine RIGHT\ndiff --git a/t/t4018/fortran-comment-legacy.ctx b/t/t4018/fortran-comment-legacy.ctx\ndeleted file mode 100644\nindex 6a34203f80..0000000000\n--- a/t/t4018/fortran-comment-legacy.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-subroutine RIGHT\ndiff --git a/t/t4018/fortran-comment.ctx b/t/t4018/fortran-comment.ctx\ndeleted file mode 100644\nindex 6a34203f80..0000000000\n--- a/t/t4018/fortran-comment.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-subroutine RIGHT\ndiff --git a/t/t4018/fortran-external-function b/t/t4018/fortran-external-function\ndeleted file mode 100644\nindex 5a2d85d3aa..0000000000\n--- a/t/t4018/fortran-external-function\n+++ /dev/null\n@@ -1,9 +0,0 @@\n-function RIGHT(a, b) result(c)\n-\n-integer, intent(in) :: ChangeMe\n-integer, intent(in) :: b\n-integer, intent(out) :: c\n-\n-c = a+b\n-\n-end function RIGHT\ndiff --git a/t/t4018/fortran-external-function.ctx b/t/t4018/fortran-external-function.ctx\ndeleted file mode 100644\nindex 56ec4d8eca..0000000000\n--- a/t/t4018/fortran-external-function.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-function RIGHT(a, b) result(c)\ndiff --git a/t/t4018/fortran-external-subroutine b/t/t4018/fortran-external-subroutine\ndeleted file mode 100644\nindex 4ce85fea13..0000000000\n--- a/t/t4018/fortran-external-subroutine\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-subroutine RIGHT\n-\n-real ChangeMe\n-\n-end subroutine RIGHT\ndiff --git a/t/t4018/fortran-external-subroutine.ctx b/t/t4018/fortran-external-subroutine.ctx\ndeleted file mode 100644\nindex 6a34203f80..0000000000\n--- a/t/t4018/fortran-external-subroutine.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-subroutine RIGHT\ndiff --git a/t/t4018/fortran-module b/t/t4018/fortran-module\ndeleted file mode 100644\nindex c4b737dac3..0000000000\n--- a/t/t4018/fortran-module\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-module RIGHT\n-\n-use ChangeMe\n-\n-end module RIGHT\ndiff --git a/t/t4018/fortran-module-procedure b/t/t4018/fortran-module-procedure\ndeleted file mode 100644\nindex 1ce6d854c2..0000000000\n--- a/t/t4018/fortran-module-procedure\n+++ /dev/null\n@@ -1,13 +0,0 @@\n- module RIGHT\n-\n-   implicit none\n-   private\n-\n-   interface letters  ! generic interface\n-      module procedure aaaa, &\n-                       bbbb, &\n-                       ChangeMe, &\n-                       dddd\n-   end interface\n-   \n-end module RIGHT\ndiff --git a/t/t4018/fortran-module-procedure.ctx b/t/t4018/fortran-module-procedure.ctx\ndeleted file mode 100644\nindex 4f5ff2e4b8..0000000000\n--- a/t/t4018/fortran-module-procedure.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-module RIGHT\ndiff --git a/t/t4018/fortran-module.ctx b/t/t4018/fortran-module.ctx\ndeleted file mode 100644\nindex 4f5ff2e4b8..0000000000\n--- a/t/t4018/fortran-module.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-module RIGHT\ndiff --git a/t/t4018/fortran-program b/t/t4018/fortran-program\ndeleted file mode 100644\nindex 4616895e4b..0000000000\n--- a/t/t4018/fortran-program\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-program RIGHT\n-\n-call ChangeMe\n-\n-end program RIGHT\ndiff --git a/t/t4018/fortran-program.ctx b/t/t4018/fortran-program.ctx\ndeleted file mode 100644\nindex c4e844df30..0000000000\n--- a/t/t4018/fortran-program.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-program RIGHT\ndiff --git a/t/t4018/fortran.sh b/t/t4018/fortran.sh\nnew file mode 100755\nindex 0000000000..7b0c6789d3\n--- /dev/null\n+++ b/t/t4018/fortran.sh\n@@ -0,0 +1,159 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'fortran: block data' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+BLOCK DATA RIGHT\n+EOF_HUNK\n+       BLOCK DATA RIGHT\n+       \n+       COMMON /B/ C, ChangeMe\n+       DATA C, ChangeMe  / 2.0, 6.0 / \n+       END \n+EOF_TEST\n+\n+test_diff_funcname 'fortran: comment' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+subroutine RIGHT\n+EOF_HUNK\n+      module a\n+\n+      contains\n+\n+      ! subroutine wrong\n+      subroutine RIGHT\n+      ! subroutine wrong\n+\n+      real ChangeMe\n+\n+      end subroutine RIGHT\n+\n+      end module a\n+EOF_TEST\n+\n+test_diff_funcname 'fortran: comment keyword' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+subroutine RIGHT (funcA, funcB)\n+EOF_HUNK\n+      module a\n+\n+      contains\n+\n+      subroutine RIGHT (funcA, funcB)\n+\n+      real funcA  ! grid function a\n+      real funcB  ! grid function b\n+\n+      real ChangeMe\n+\n+      end subroutine RIGHT\n+\n+      end module a\n+EOF_TEST\n+\n+test_diff_funcname 'fortran: comment legacy' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+subroutine RIGHT\n+EOF_HUNK\n+      module a\n+\n+      contains\n+\n+C subroutine wrong\n+      subroutine RIGHT\n+C subroutine wrong\n+\n+      real ChangeMe\n+\n+      end subroutine RIGHT\n+\n+      end module a\n+EOF_TEST\n+\n+test_diff_funcname 'fortran: comment legacy star' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+subroutine RIGHT\n+EOF_HUNK\n+      module a\n+\n+      contains\n+\n+* subroutine wrong\n+      subroutine RIGHT\n+* subroutine wrong\n+\n+      real ChangeMe\n+\n+      end subroutine RIGHT\n+\n+      end module a\n+EOF_TEST\n+\n+test_diff_funcname 'fortran: external function' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+function RIGHT(a, b) result(c)\n+EOF_HUNK\n+function RIGHT(a, b) result(c)\n+\n+integer, intent(in) :: ChangeMe\n+integer, intent(in) :: b\n+integer, intent(out) :: c\n+\n+c = a+b\n+\n+end function RIGHT\n+EOF_TEST\n+\n+test_diff_funcname 'fortran: external subroutine' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+subroutine RIGHT\n+EOF_HUNK\n+subroutine RIGHT\n+\n+real ChangeMe\n+\n+end subroutine RIGHT\n+EOF_TEST\n+\n+test_diff_funcname 'fortran: module' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+module RIGHT\n+EOF_HUNK\n+module RIGHT\n+\n+use ChangeMe\n+\n+end module RIGHT\n+EOF_TEST\n+\n+test_diff_funcname 'fortran: module procedure' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+module RIGHT\n+EOF_HUNK\n+ module RIGHT\n+\n+   implicit none\n+   private\n+\n+   interface letters  ! generic interface\n+      module procedure aaaa, &\n+                       bbbb, &\n+                       ChangeMe, &\n+                       dddd\n+   end interface\n+   \n+end module RIGHT\n+EOF_TEST\n+\n+test_diff_funcname 'fortran: program' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+program RIGHT\n+EOF_HUNK\n+program RIGHT\n+\n+call ChangeMe\n+\n+end program RIGHT\n+EOF_TEST\ndiff --git a/t/t4018/fountain-scene b/t/t4018/fountain-scene\ndeleted file mode 100644\nindex 6b3257d680..0000000000\n--- a/t/t4018/fountain-scene\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-EXT. STREET RIGHT OUTSIDE - DAY\n-\n-CHARACTER\n-You didn't say the magic phrase, \"ChangeMe\".\ndiff --git a/t/t4018/fountain-scene.ctx b/t/t4018/fountain-scene.ctx\ndeleted file mode 100644\nindex bf10171418..0000000000\n--- a/t/t4018/fountain-scene.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-EXT. STREET RIGHT OUTSIDE - DAY\ndiff --git a/t/t4018/fountain.sh b/t/t4018/fountain.sh\nnew file mode 100755\nindex 0000000000..02b44d6a3f\n--- /dev/null\n+++ b/t/t4018/fountain.sh\n@@ -0,0 +1,14 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'fountain: scene' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+EXT. STREET RIGHT OUTSIDE - DAY\n+EOF_HUNK\n+EXT. STREET RIGHT OUTSIDE - DAY\n+\n+CHARACTER\n+You didn't say the magic phrase, \"ChangeMe\".\n+EOF_TEST\ndiff --git a/t/t4018/golang-complex-function b/t/t4018/golang-complex-function\ndeleted file mode 100644\nindex e057dcefed..0000000000\n--- a/t/t4018/golang-complex-function\n+++ /dev/null\n@@ -1,8 +0,0 @@\n-type Test struct {\n-\ta Type\n-}\n-\n-func (t *Test) RIGHT(a Type) (Type, error) {\n-\tt.a = a\n-\treturn ChangeMe, nil\n-}\ndiff --git a/t/t4018/golang-complex-function.ctx b/t/t4018/golang-complex-function.ctx\ndeleted file mode 100644\nindex 8e8d5582ff..0000000000\n--- a/t/t4018/golang-complex-function.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-func (t *Test) RIGHT(a Type) (Type, error) {\ndiff --git a/t/t4018/golang-func b/t/t4018/golang-func\ndeleted file mode 100644\nindex 8e9c9ac7c3..0000000000\n--- a/t/t4018/golang-func\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-func RIGHT() {\n-\ta := 5\n-\tb := ChangeMe\n-}\ndiff --git a/t/t4018/golang-func.ctx b/t/t4018/golang-func.ctx\ndeleted file mode 100644\nindex 88bc823813..0000000000\n--- a/t/t4018/golang-func.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-func RIGHT() {\ndiff --git a/t/t4018/golang-interface b/t/t4018/golang-interface\ndeleted file mode 100644\nindex 553bedec96..0000000000\n--- a/t/t4018/golang-interface\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-type RIGHT interface {\n-\ta() Type\n-\tb() ChangeMe\n-}\ndiff --git a/t/t4018/golang-interface.ctx b/t/t4018/golang-interface.ctx\ndeleted file mode 100644\nindex 2d07f5a383..0000000000\n--- a/t/t4018/golang-interface.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-type RIGHT interface {\ndiff --git a/t/t4018/golang-long-func b/t/t4018/golang-long-func\ndeleted file mode 100644\nindex ac3a77b5c4..0000000000\n--- a/t/t4018/golang-long-func\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-func RIGHT(aVeryVeryVeryLongVariableName AVeryVeryVeryLongType,\n-\tanotherLongVariableName AnotherLongType) {\n-\ta := 5\n-\tb := ChangeMe\n-}\ndiff --git a/t/t4018/golang-long-func.ctx b/t/t4018/golang-long-func.ctx\ndeleted file mode 100644\nindex 25635e712e..0000000000\n--- a/t/t4018/golang-long-func.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-func RIGHT(aVeryVeryVeryLongVariableName AVeryVeryVeryLongType,\ndiff --git a/t/t4018/golang-struct b/t/t4018/golang-struct\ndeleted file mode 100644\nindex 5deda77fee..0000000000\n--- a/t/t4018/golang-struct\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-type RIGHT struct {\n-\ta Type\n-\tb ChangeMe\n-}\ndiff --git a/t/t4018/golang-struct.ctx b/t/t4018/golang-struct.ctx\ndeleted file mode 100644\nindex 8a1240699d..0000000000\n--- a/t/t4018/golang-struct.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-type RIGHT struct {\ndiff --git a/t/t4018/golang.sh b/t/t4018/golang.sh\nnew file mode 100755\nindex 0000000000..bf22f58c12\n--- /dev/null\n+++ b/t/t4018/golang.sh\n@@ -0,0 +1,59 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'golang: complex function' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+func (t *Test) RIGHT(a Type) (Type, error) {\n+EOF_HUNK\n+type Test struct {\n+\ta Type\n+}\n+\n+func (t *Test) RIGHT(a Type) (Type, error) {\n+\tt.a = a\n+\treturn ChangeMe, nil\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'golang: func' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+func RIGHT() {\n+EOF_HUNK\n+func RIGHT() {\n+\ta := 5\n+\tb := ChangeMe\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'golang: interface' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+type RIGHT interface {\n+EOF_HUNK\n+type RIGHT interface {\n+\ta() Type\n+\tb() ChangeMe\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'golang: long func' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+func RIGHT(aVeryVeryVeryLongVariableName AVeryVeryVeryLongType,\n+EOF_HUNK\n+func RIGHT(aVeryVeryVeryLongVariableName AVeryVeryVeryLongType,\n+\tanotherLongVariableName AnotherLongType) {\n+\ta := 5\n+\tb := ChangeMe\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'golang: struct' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+type RIGHT struct {\n+EOF_HUNK\n+type RIGHT struct {\n+\ta Type\n+\tb ChangeMe\n+}\n+EOF_TEST\ndiff --git a/t/t4018/java-class-member-function b/t/t4018/java-class-member-function\ndeleted file mode 100644\nindex 298bc7a71b..0000000000\n--- a/t/t4018/java-class-member-function\n+++ /dev/null\n@@ -1,8 +0,0 @@\n-public class Beer\n-{\n-\tint special;\n-\tpublic static void main(String RIGHT[])\n-\t{\n-\t\tSystem.out.print(\"ChangeMe\");\n-\t}\n-}\ndiff --git a/t/t4018/java-class-member-function.ctx b/t/t4018/java-class-member-function.ctx\ndeleted file mode 100644\nindex 2125474b68..0000000000\n--- a/t/t4018/java-class-member-function.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-public static void main(String RIGHT[])\ndiff --git a/t/t4018/java.sh b/t/t4018/java.sh\nnew file mode 100755\nindex 0000000000..c89cf2f9d8\n--- /dev/null\n+++ b/t/t4018/java.sh\n@@ -0,0 +1,18 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'java: class member function' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+public static void main(String RIGHT[])\n+EOF_HUNK\n+public class Beer\n+{\n+\tint special;\n+\tpublic static void main(String RIGHT[])\n+\t{\n+\t\tSystem.out.print(\"ChangeMe\");\n+\t}\n+}\n+EOF_TEST\ndiff --git a/t/t4018/markdown-heading-indented b/t/t4018/markdown-heading-indented\ndeleted file mode 100644\nindex 1991c2bd45..0000000000\n--- a/t/t4018/markdown-heading-indented\n+++ /dev/null\n@@ -1,6 +0,0 @@\n-Indented headings are allowed, as long as the indent is no more than 3 spaces.\n-\n-   ### RIGHT\n-\n-- something\n-- ChangeMe\ndiff --git a/t/t4018/markdown-heading-indented.ctx b/t/t4018/markdown-heading-indented.ctx\ndeleted file mode 100644\nindex 5938336743..0000000000\n--- a/t/t4018/markdown-heading-indented.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-   ### RIGHT\ndiff --git a/t/t4018/markdown-heading-non-headings b/t/t4018/markdown-heading-non-headings\ndeleted file mode 100644\nindex c479c1a3f1..0000000000\n--- a/t/t4018/markdown-heading-non-headings\n+++ /dev/null\n@@ -1,17 +0,0 @@\n-Headings can be right next to other lines of the file:\n-# RIGHT\n-Indents of four or more spaces make a code block:\n-\n-    # code comment, not heading\n-\n-If there's no space after the final hash, it's not a heading:\n-\n-#hashtag\n-\n-Sequences of more than 6 hashes don't make a heading:\n-\n-####### over-enthusiastic heading\n-\n-So the detected heading should be right up at the start of this file.\n-\n-ChangeMe\ndiff --git a/t/t4018/markdown-heading-non-headings.ctx b/t/t4018/markdown-heading-non-headings.ctx\ndeleted file mode 100644\nindex 7e2165be6e..0000000000\n--- a/t/t4018/markdown-heading-non-headings.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-# RIGHT\ndiff --git a/t/t4018/markdown.sh b/t/t4018/markdown.sh\nnew file mode 100755\nindex 0000000000..3e1c79b139\n--- /dev/null\n+++ b/t/t4018/markdown.sh\n@@ -0,0 +1,39 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'markdown: heading indented' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+   ### RIGHT\n+EOF_HUNK\n+Indented headings are allowed, as long as the indent is no more than 3 spaces.\n+\n+   ### RIGHT\n+\n+- something\n+- ChangeMe\n+EOF_TEST\n+\n+test_diff_funcname 'markdown: heading non headings' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+# RIGHT\n+EOF_HUNK\n+Headings can be right next to other lines of the file:\n+# RIGHT\n+Indents of four or more spaces make a code block:\n+\n+    # code comment, not heading\n+\n+If there's no space after the final hash, it's not a heading:\n+\n+#hashtag\n+\n+Sequences of more than 6 hashes don't make a heading:\n+\n+####### over-enthusiastic heading\n+\n+So the detected heading should be right up at the start of this file.\n+\n+ChangeMe\n+EOF_TEST\ndiff --git a/t/t4018/matlab-class-definition b/t/t4018/matlab-class-definition\ndeleted file mode 100644\nindex 84daedfb4e..0000000000\n--- a/t/t4018/matlab-class-definition\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-classdef RIGHT\n-    properties\n-        ChangeMe\n-    end\n-end\ndiff --git a/t/t4018/matlab-class-definition.ctx b/t/t4018/matlab-class-definition.ctx\ndeleted file mode 100644\nindex 5dd5b45628..0000000000\n--- a/t/t4018/matlab-class-definition.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-classdef RIGHT\ndiff --git a/t/t4018/matlab-function b/t/t4018/matlab-function\ndeleted file mode 100644\nindex 897a9b13ff..0000000000\n--- a/t/t4018/matlab-function\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-function y = RIGHT()\n-x = 5;\n-y = ChangeMe + x;\n-end\ndiff --git a/t/t4018/matlab-function.ctx b/t/t4018/matlab-function.ctx\ndeleted file mode 100644\nindex 72d2350b13..0000000000\n--- a/t/t4018/matlab-function.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-function y = RIGHT()\ndiff --git a/t/t4018/matlab-octave-section-1 b/t/t4018/matlab-octave-section-1\ndeleted file mode 100644\nindex 3bb6c4670e..0000000000\n--- a/t/t4018/matlab-octave-section-1\n+++ /dev/null\n@@ -1,3 +0,0 @@\n-%%% RIGHT section\n-# this is octave script\n-ChangeMe = 1;\ndiff --git a/t/t4018/matlab-octave-section-1.ctx b/t/t4018/matlab-octave-section-1.ctx\ndeleted file mode 100644\nindex ca9b349f94..0000000000\n--- a/t/t4018/matlab-octave-section-1.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-%%% RIGHT section\ndiff --git a/t/t4018/matlab-octave-section-2 b/t/t4018/matlab-octave-section-2\ndeleted file mode 100644\nindex ab2980f7f2..0000000000\n--- a/t/t4018/matlab-octave-section-2\n+++ /dev/null\n@@ -1,3 +0,0 @@\n-## RIGHT section\n-# this is octave script\n-ChangeMe = 1;\ndiff --git a/t/t4018/matlab-octave-section-2.ctx b/t/t4018/matlab-octave-section-2.ctx\ndeleted file mode 100644\nindex 5cbb77faf5..0000000000\n--- a/t/t4018/matlab-octave-section-2.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-## RIGHT section\ndiff --git a/t/t4018/matlab-section b/t/t4018/matlab-section\ndeleted file mode 100644\nindex 5ea59a5de0..0000000000\n--- a/t/t4018/matlab-section\n+++ /dev/null\n@@ -1,3 +0,0 @@\n-%% RIGHT section\n-% this is understood by both matlab and octave\n-ChangeMe = 1;\ndiff --git a/t/t4018/matlab-section.ctx b/t/t4018/matlab-section.ctx\ndeleted file mode 100644\nindex e83fee6f4d..0000000000\n--- a/t/t4018/matlab-section.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-%% RIGHT section\ndiff --git a/t/t4018/matlab.sh b/t/t4018/matlab.sh\nnew file mode 100755\nindex 0000000000..f62289148e\n--- /dev/null\n+++ b/t/t4018/matlab.sh\n@@ -0,0 +1,52 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'matlab: class definition' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+classdef RIGHT\n+EOF_HUNK\n+classdef RIGHT\n+    properties\n+        ChangeMe\n+    end\n+end\n+EOF_TEST\n+\n+test_diff_funcname 'matlab: function' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+function y = RIGHT()\n+EOF_HUNK\n+function y = RIGHT()\n+x = 5;\n+y = ChangeMe + x;\n+end\n+EOF_TEST\n+\n+test_diff_funcname 'matlab: octave section 1' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+%%% RIGHT section\n+EOF_HUNK\n+%%% RIGHT section\n+# this is octave script\n+ChangeMe = 1;\n+EOF_TEST\n+\n+test_diff_funcname 'matlab: octave section 2' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+## RIGHT section\n+EOF_HUNK\n+## RIGHT section\n+# this is octave script\n+ChangeMe = 1;\n+EOF_TEST\n+\n+test_diff_funcname 'matlab: section' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+%% RIGHT section\n+EOF_HUNK\n+%% RIGHT section\n+% this is understood by both matlab and octave\n+ChangeMe = 1;\n+EOF_TEST\ndiff --git a/t/t4018/perl-skip-end-of-heredoc b/t/t4018/perl-skip-end-of-heredoc\ndeleted file mode 100644\nindex c22d39b256..0000000000\n--- a/t/t4018/perl-skip-end-of-heredoc\n+++ /dev/null\n@@ -1,8 +0,0 @@\n-sub RIGHTwithheredocument {\n-\tprint <<\"EOF\"\n-decoy here-doc\n-EOF\n-\t# some lines of context\n-\t# to pad it out\n-\tprint \"ChangeMe\\n\";\n-}\ndiff --git a/t/t4018/perl-skip-end-of-heredoc.ctx b/t/t4018/perl-skip-end-of-heredoc.ctx\ndeleted file mode 100644\nindex c15f4b78bd..0000000000\n--- a/t/t4018/perl-skip-end-of-heredoc.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-sub RIGHTwithheredocument {\ndiff --git a/t/t4018/perl-skip-forward-decl b/t/t4018/perl-skip-forward-decl\ndeleted file mode 100644\nindex a98cb8bdad..0000000000\n--- a/t/t4018/perl-skip-forward-decl\n+++ /dev/null\n@@ -1,10 +0,0 @@\n-package RIGHT;\n-\n-use strict;\n-use warnings;\n-use parent qw(Exporter);\n-our @EXPORT_OK = qw(round finalround);\n-\n-sub other; # forward declaration\n-\n-# ChangeMe\ndiff --git a/t/t4018/perl-skip-forward-decl.ctx b/t/t4018/perl-skip-forward-decl.ctx\ndeleted file mode 100644\nindex e0c51599ad..0000000000\n--- a/t/t4018/perl-skip-forward-decl.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-package RIGHT;\ndiff --git a/t/t4018/perl-skip-sub-in-pod b/t/t4018/perl-skip-sub-in-pod\ndeleted file mode 100644\nindex e39f02462e..0000000000\n--- a/t/t4018/perl-skip-sub-in-pod\n+++ /dev/null\n@@ -1,18 +0,0 @@\n-=head1 NAME\n-\n-Beer - subroutine to output fragment of a drinking song\n-\n-=head1 SYNOPSIS_RIGHT\n-\n-\tuse Beer qw(round finalround);\n-\n-\tsub song {\n-\t\tfor (my $i = 99; $i > 0; $i--) {\n-\t\t\tround $i;\n-\t\t}\n-\t\tfinalround;\n-\t}\n-\n-\tChangeMe;\n-\n-=cut\ndiff --git a/t/t4018/perl-skip-sub-in-pod.ctx b/t/t4018/perl-skip-sub-in-pod.ctx\ndeleted file mode 100644\nindex abddd76655..0000000000\n--- a/t/t4018/perl-skip-sub-in-pod.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-=head1 SYNOPSIS_RIGHT\ndiff --git a/t/t4018/perl-sub-definition b/t/t4018/perl-sub-definition\ndeleted file mode 100644\nindex a507d1f645..0000000000\n--- a/t/t4018/perl-sub-definition\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-sub RIGHT {\n-\tmy ($n) = @_;\n-\tprint \"ChangeMe\";\n-}\ndiff --git a/t/t4018/perl-sub-definition-kr-brace b/t/t4018/perl-sub-definition-kr-brace\ndeleted file mode 100644\nindex 330b3df114..0000000000\n--- a/t/t4018/perl-sub-definition-kr-brace\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-sub RIGHT\n-{\n-\tprint \"ChangeMe\\n\";\n-}\ndiff --git a/t/t4018/perl-sub-definition-kr-brace.ctx b/t/t4018/perl-sub-definition-kr-brace.ctx\ndeleted file mode 100644\nindex 7e5aee5cde..0000000000\n--- a/t/t4018/perl-sub-definition-kr-brace.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-sub RIGHT\ndiff --git a/t/t4018/perl-sub-definition.ctx b/t/t4018/perl-sub-definition.ctx\ndeleted file mode 100644\nindex d49a63598e..0000000000\n--- a/t/t4018/perl-sub-definition.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-sub RIGHT {\ndiff --git a/t/t4018/perl.sh b/t/t4018/perl.sh\nnew file mode 100755\nindex 0000000000..ac8fff7417\n--- /dev/null\n+++ b/t/t4018/perl.sh\n@@ -0,0 +1,78 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'perl: skip end of heredoc' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+sub RIGHTwithheredocument {\n+EOF_HUNK\n+sub RIGHTwithheredocument {\n+\tprint <<\"EOF\"\n+decoy here-doc\n+EOF\n+\t# some lines of context\n+\t# to pad it out\n+\tprint \"ChangeMe\\n\";\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'perl: skip forward decl' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+package RIGHT;\n+EOF_HUNK\n+package RIGHT;\n+\n+use strict;\n+use warnings;\n+use parent qw(Exporter);\n+our @EXPORT_OK = qw(round finalround);\n+\n+sub other; # forward declaration\n+\n+# ChangeMe\n+EOF_TEST\n+\n+test_diff_funcname 'perl: skip sub in pod' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+=head1 SYNOPSIS_RIGHT\n+EOF_HUNK\n+=head1 NAME\n+\n+Beer - subroutine to output fragment of a drinking song\n+\n+=head1 SYNOPSIS_RIGHT\n+\n+\tuse Beer qw(round finalround);\n+\n+\tsub song {\n+\t\tfor (my $i = 99; $i > 0; $i--) {\n+\t\t\tround $i;\n+\t\t}\n+\t\tfinalround;\n+\t}\n+\n+\tChangeMe;\n+\n+=cut\n+EOF_TEST\n+\n+test_diff_funcname 'perl: sub definition' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+sub RIGHT {\n+EOF_HUNK\n+sub RIGHT {\n+\tmy ($n) = @_;\n+\tprint \"ChangeMe\";\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'perl: sub definition kr brace' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+sub RIGHT\n+EOF_HUNK\n+sub RIGHT\n+{\n+\tprint \"ChangeMe\\n\";\n+}\n+EOF_TEST\ndiff --git a/t/t4018/php-abstract-class b/t/t4018/php-abstract-class\ndeleted file mode 100644\nindex 5213e12494..0000000000\n--- a/t/t4018/php-abstract-class\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-abstract class RIGHT\n-{\n-    const FOO = 'ChangeMe';\n-}\ndiff --git a/t/t4018/php-abstract-class.ctx b/t/t4018/php-abstract-class.ctx\ndeleted file mode 100644\nindex f572d2129b..0000000000\n--- a/t/t4018/php-abstract-class.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-abstract class RIGHT\ndiff --git a/t/t4018/php-abstract-method b/t/t4018/php-abstract-method\ndeleted file mode 100644\nindex ce215df75a..0000000000\n--- a/t/t4018/php-abstract-method\n+++ /dev/null\n@@ -1,7 +0,0 @@\n-abstract class Klass\n-{\n-    abstract public function RIGHT(): ?string\n-    {\n-        return 'ChangeMe';\n-    }\n-}\ndiff --git a/t/t4018/php-abstract-method.ctx b/t/t4018/php-abstract-method.ctx\ndeleted file mode 100644\nindex 14cb6df42e..0000000000\n--- a/t/t4018/php-abstract-method.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-abstract public function RIGHT(): ?string\ndiff --git a/t/t4018/php-class b/t/t4018/php-class\ndeleted file mode 100644\nindex 7785b6303c..0000000000\n--- a/t/t4018/php-class\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-class RIGHT\n-{\n-    const FOO = 'ChangeMe';\n-}\ndiff --git a/t/t4018/php-class.ctx b/t/t4018/php-class.ctx\ndeleted file mode 100644\nindex 54bff816d6..0000000000\n--- a/t/t4018/php-class.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-class RIGHT\ndiff --git a/t/t4018/php-final-class b/t/t4018/php-final-class\ndeleted file mode 100644\nindex 69f5710552..0000000000\n--- a/t/t4018/php-final-class\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-final class RIGHT\n-{\n-    const FOO = 'ChangeMe';\n-}\ndiff --git a/t/t4018/php-final-class.ctx b/t/t4018/php-final-class.ctx\ndeleted file mode 100644\nindex 4d59fb749b..0000000000\n--- a/t/t4018/php-final-class.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-final class RIGHT\ndiff --git a/t/t4018/php-final-method b/t/t4018/php-final-method\ndeleted file mode 100644\nindex 537fb8ad9a..0000000000\n--- a/t/t4018/php-final-method\n+++ /dev/null\n@@ -1,7 +0,0 @@\n-class Klass\n-{\n-    final public function RIGHT(): string\n-    {\n-        return 'ChangeMe';\n-    }\n-}\ndiff --git a/t/t4018/php-final-method.ctx b/t/t4018/php-final-method.ctx\ndeleted file mode 100644\nindex b7da8f8082..0000000000\n--- a/t/t4018/php-final-method.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-final public function RIGHT(): string\ndiff --git a/t/t4018/php-function b/t/t4018/php-function\ndeleted file mode 100644\nindex 35717c51c3..0000000000\n--- a/t/t4018/php-function\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-function RIGHT()\n-{\n-    return 'ChangeMe';\n-}\ndiff --git a/t/t4018/php-function.ctx b/t/t4018/php-function.ctx\ndeleted file mode 100644\nindex c5f3e55302..0000000000\n--- a/t/t4018/php-function.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-function RIGHT()\ndiff --git a/t/t4018/php-interface b/t/t4018/php-interface\ndeleted file mode 100644\nindex 86b49ad5d9..0000000000\n--- a/t/t4018/php-interface\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-interface RIGHT\n-{\n-    public function foo($ChangeMe);\n-}\ndiff --git a/t/t4018/php-interface.ctx b/t/t4018/php-interface.ctx\ndeleted file mode 100644\nindex a45fa0532a..0000000000\n--- a/t/t4018/php-interface.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-interface RIGHT\ndiff --git a/t/t4018/php-method b/t/t4018/php-method\ndeleted file mode 100644\nindex 03af1a6d9d..0000000000\n--- a/t/t4018/php-method\n+++ /dev/null\n@@ -1,7 +0,0 @@\n-class Klass\n-{\n-    public static function RIGHT()\n-    {\n-        return 'ChangeMe';\n-    }\n-}\ndiff --git a/t/t4018/php-method.ctx b/t/t4018/php-method.ctx\ndeleted file mode 100644\nindex eb1659ff9f..0000000000\n--- a/t/t4018/php-method.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-public static function RIGHT()\ndiff --git a/t/t4018/php-trait b/t/t4018/php-trait\ndeleted file mode 100644\nindex 65b8c82a61..0000000000\n--- a/t/t4018/php-trait\n+++ /dev/null\n@@ -1,7 +0,0 @@\n-trait RIGHT\n-{\n-    public function foo($ChangeMe)\n-    {\n-        return 'foo';\n-    }\n-}\ndiff --git a/t/t4018/php-trait.ctx b/t/t4018/php-trait.ctx\ndeleted file mode 100644\nindex 57aa4c6267..0000000000\n--- a/t/t4018/php-trait.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-trait RIGHT\ndiff --git a/t/t4018/php.sh b/t/t4018/php.sh\nnew file mode 100755\nindex 0000000000..e0ccb2277b\n--- /dev/null\n+++ b/t/t4018/php.sh\n@@ -0,0 +1,106 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'php: abstract class' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+abstract class RIGHT\n+EOF_HUNK\n+abstract class RIGHT\n+{\n+    const FOO = 'ChangeMe';\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'php: abstract method' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+abstract public function RIGHT(): ?string\n+EOF_HUNK\n+abstract class Klass\n+{\n+    abstract public function RIGHT(): ?string\n+    {\n+        return 'ChangeMe';\n+    }\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'php: class' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+class RIGHT\n+EOF_HUNK\n+class RIGHT\n+{\n+    const FOO = 'ChangeMe';\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'php: final class' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+final class RIGHT\n+EOF_HUNK\n+final class RIGHT\n+{\n+    const FOO = 'ChangeMe';\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'php: final method' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+final public function RIGHT(): string\n+EOF_HUNK\n+class Klass\n+{\n+    final public function RIGHT(): string\n+    {\n+        return 'ChangeMe';\n+    }\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'php: function' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+function RIGHT()\n+EOF_HUNK\n+function RIGHT()\n+{\n+    return 'ChangeMe';\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'php: interface' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+interface RIGHT\n+EOF_HUNK\n+interface RIGHT\n+{\n+    public function foo($ChangeMe);\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'php: method' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+public static function RIGHT()\n+EOF_HUNK\n+class Klass\n+{\n+    public static function RIGHT()\n+    {\n+        return 'ChangeMe';\n+    }\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'php: trait' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+trait RIGHT\n+EOF_HUNK\n+trait RIGHT\n+{\n+    public function foo($ChangeMe)\n+    {\n+        return 'foo';\n+    }\n+}\n+EOF_TEST\ndiff --git a/t/t4018/python-async-def b/t/t4018/python-async-def\ndeleted file mode 100644\nindex 87640e03d2..0000000000\n--- a/t/t4018/python-async-def\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-async def RIGHT(pi: int = 3.14):\n-    while True:\n-        break\n-    return ChangeMe()\ndiff --git a/t/t4018/python-async-def.ctx b/t/t4018/python-async-def.ctx\ndeleted file mode 100644\nindex 468c548bbe..0000000000\n--- a/t/t4018/python-async-def.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-async def RIGHT(pi: int = 3.14):\ndiff --git a/t/t4018/python-class b/t/t4018/python-class\ndeleted file mode 100644\nindex ba9e741430..0000000000\n--- a/t/t4018/python-class\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-class RIGHT(int, str):\n-    # comment\n-    # another comment\n-    # ChangeMe\ndiff --git a/t/t4018/python-class.ctx b/t/t4018/python-class.ctx\ndeleted file mode 100644\nindex a40b755e29..0000000000\n--- a/t/t4018/python-class.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-class RIGHT(int, str):\ndiff --git a/t/t4018/python-def b/t/t4018/python-def\ndeleted file mode 100644\nindex e50b31b0ad..0000000000\n--- a/t/t4018/python-def\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-def RIGHT(pi: int = 3.14):\n-    while True:\n-        break\n-    return ChangeMe()\ndiff --git a/t/t4018/python-def.ctx b/t/t4018/python-def.ctx\ndeleted file mode 100644\nindex a1a9cbad63..0000000000\n--- a/t/t4018/python-def.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-def RIGHT(pi: int = 3.14):\ndiff --git a/t/t4018/python-indented-async-def b/t/t4018/python-indented-async-def\ndeleted file mode 100644\nindex f5d03258af..0000000000\n--- a/t/t4018/python-indented-async-def\n+++ /dev/null\n@@ -1,7 +0,0 @@\n-class Foo:\n-    async def RIGHT(self, x: int):\n-        return [\n-            1,\n-            2,\n-            ChangeMe,\n-        ]\ndiff --git a/t/t4018/python-indented-async-def.ctx b/t/t4018/python-indented-async-def.ctx\ndeleted file mode 100644\nindex d393620a1e..0000000000\n--- a/t/t4018/python-indented-async-def.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-async def RIGHT(self, x: int):\ndiff --git a/t/t4018/python-indented-class b/t/t4018/python-indented-class\ndeleted file mode 100644\nindex 19b4f35c4c..0000000000\n--- a/t/t4018/python-indented-class\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-if TYPE_CHECKING:\n-    class RIGHT:\n-        # comment\n-        # another comment\n-        # ChangeMe\ndiff --git a/t/t4018/python-indented-class.ctx b/t/t4018/python-indented-class.ctx\ndeleted file mode 100644\nindex 0881c84dba..0000000000\n--- a/t/t4018/python-indented-class.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-class RIGHT:\ndiff --git a/t/t4018/python-indented-def b/t/t4018/python-indented-def\ndeleted file mode 100644\nindex 208fbadd2b..0000000000\n--- a/t/t4018/python-indented-def\n+++ /dev/null\n@@ -1,7 +0,0 @@\n-class Foo:\n-    def RIGHT(self, x: int):\n-        return [\n-            1,\n-            2,\n-            ChangeMe,\n-        ]\ndiff --git a/t/t4018/python-indented-def.ctx b/t/t4018/python-indented-def.ctx\ndeleted file mode 100644\nindex 6e5a44b391..0000000000\n--- a/t/t4018/python-indented-def.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-def RIGHT(self, x: int):\ndiff --git a/t/t4018/python.sh b/t/t4018/python.sh\nnew file mode 100755\nindex 0000000000..ecb5736d57\n--- /dev/null\n+++ b/t/t4018/python.sh\n@@ -0,0 +1,71 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'python: async def' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+async def RIGHT(pi: int = 3.14):\n+EOF_HUNK\n+async def RIGHT(pi: int = 3.14):\n+    while True:\n+        break\n+    return ChangeMe()\n+EOF_TEST\n+\n+test_diff_funcname 'python: class' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+class RIGHT(int, str):\n+EOF_HUNK\n+class RIGHT(int, str):\n+    # comment\n+    # another comment\n+    # ChangeMe\n+EOF_TEST\n+\n+test_diff_funcname 'python: def' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+def RIGHT(pi: int = 3.14):\n+EOF_HUNK\n+def RIGHT(pi: int = 3.14):\n+    while True:\n+        break\n+    return ChangeMe()\n+EOF_TEST\n+\n+test_diff_funcname 'python: indented async def' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+async def RIGHT(self, x: int):\n+EOF_HUNK\n+class Foo:\n+    async def RIGHT(self, x: int):\n+        return [\n+            1,\n+            2,\n+            ChangeMe,\n+        ]\n+EOF_TEST\n+\n+test_diff_funcname 'python: indented class' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+class RIGHT:\n+EOF_HUNK\n+if TYPE_CHECKING:\n+    class RIGHT:\n+        # comment\n+        # another comment\n+        # ChangeMe\n+EOF_TEST\n+\n+test_diff_funcname 'python: indented def' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+def RIGHT(self, x: int):\n+EOF_HUNK\n+class Foo:\n+    def RIGHT(self, x: int):\n+        return [\n+            1,\n+            2,\n+            ChangeMe,\n+        ]\n+EOF_TEST\ndiff --git a/t/t4018/rust-fn b/t/t4018/rust-fn\ndeleted file mode 100644\nindex cbe02155f1..0000000000\n--- a/t/t4018/rust-fn\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-pub(self) fn RIGHT<T>(x: &[T]) where T: Debug {\n-    let _ = x;\n-    // a comment\n-    let a = ChangeMe;\n-}\ndiff --git a/t/t4018/rust-fn.ctx b/t/t4018/rust-fn.ctx\ndeleted file mode 100644\nindex baa37cf253..0000000000\n--- a/t/t4018/rust-fn.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-pub(self) fn RIGHT<T>(x: &[T]) where T: Debug {\ndiff --git a/t/t4018/rust-impl b/t/t4018/rust-impl\ndeleted file mode 100644\nindex 09df3cd93b..0000000000\n--- a/t/t4018/rust-impl\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-impl<'a, T: AsRef<[u8]>>  std::RIGHT for Git<'a> {\n-\n-    pub fn ChangeMe(&self) -> () {\n-    }\n-}\ndiff --git a/t/t4018/rust-impl.ctx b/t/t4018/rust-impl.ctx\ndeleted file mode 100644\nindex 5344c35f3f..0000000000\n--- a/t/t4018/rust-impl.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-impl<'a, T: AsRef<[u8]>>  std::RIGHT for Git<'a> {\ndiff --git a/t/t4018/rust-macro-rules b/t/t4018/rust-macro-rules\ndeleted file mode 100644\nindex ec610c5b62..0000000000\n--- a/t/t4018/rust-macro-rules\n+++ /dev/null\n@@ -1,6 +0,0 @@\n-macro_rules! RIGHT {\n-    () => {\n-        // a comment\n-        let x = ChangeMe;\n-    };\n-}\ndiff --git a/t/t4018/rust-macro-rules.ctx b/t/t4018/rust-macro-rules.ctx\ndeleted file mode 100644\nindex 7520463aa0..0000000000\n--- a/t/t4018/rust-macro-rules.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-macro_rules! RIGHT {\ndiff --git a/t/t4018/rust-struct b/t/t4018/rust-struct\ndeleted file mode 100644\nindex 76aff1c0d8..0000000000\n--- a/t/t4018/rust-struct\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-#[derive(Debug)]\n-pub(super) struct RIGHT<'a> {\n-    name: &'a str,\n-    age: ChangeMe,\n-}\ndiff --git a/t/t4018/rust-struct.ctx b/t/t4018/rust-struct.ctx\ndeleted file mode 100644\nindex c1e09dc808..0000000000\n--- a/t/t4018/rust-struct.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-pub(super) struct RIGHT<'a> {\ndiff --git a/t/t4018/rust-trait b/t/t4018/rust-trait\ndeleted file mode 100644\nindex ea397f09ed..0000000000\n--- a/t/t4018/rust-trait\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-unsafe trait RIGHT<T> {\n-    fn len(&self) -> u32;\n-    fn ChangeMe(&self, n: u32) -> T;\n-    fn iter<F>(&self, f: F) where F: Fn(T);\n-}\ndiff --git a/t/t4018/rust-trait.ctx b/t/t4018/rust-trait.ctx\ndeleted file mode 100644\nindex 6af803db29..0000000000\n--- a/t/t4018/rust-trait.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-unsafe trait RIGHT<T> {\ndiff --git a/t/t4018/rust.sh b/t/t4018/rust.sh\nnew file mode 100755\nindex 0000000000..ba018c6b95\n--- /dev/null\n+++ b/t/t4018/rust.sh\n@@ -0,0 +1,60 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'rust: fn' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+pub(self) fn RIGHT<T>(x: &[T]) where T: Debug {\n+EOF_HUNK\n+pub(self) fn RIGHT<T>(x: &[T]) where T: Debug {\n+    let _ = x;\n+    // a comment\n+    let a = ChangeMe;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'rust: impl' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+impl<'a, T: AsRef<[u8]>>  std::RIGHT for Git<'a> {\n+EOF_HUNK\n+impl<'a, T: AsRef<[u8]>>  std::RIGHT for Git<'a> {\n+\n+    pub fn ChangeMe(&self) -> () {\n+    }\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'rust: macro rules' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+macro_rules! RIGHT {\n+EOF_HUNK\n+macro_rules! RIGHT {\n+    () => {\n+        // a comment\n+        let x = ChangeMe;\n+    };\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'rust: struct' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+pub(super) struct RIGHT<'a> {\n+EOF_HUNK\n+#[derive(Debug)]\n+pub(super) struct RIGHT<'a> {\n+    name: &'a str,\n+    age: ChangeMe,\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'rust: trait' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+unsafe trait RIGHT<T> {\n+EOF_HUNK\n+unsafe trait RIGHT<T> {\n+    fn len(&self) -> u32;\n+    fn ChangeMe(&self, n: u32) -> T;\n+    fn iter<F>(&self, f: F) where F: Fn(T);\n+}\n+EOF_TEST\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416898","messageId":"20210215005236.11313-13-avarab@gmail.com","threadId":"55144","inReplyTo":"87tuqebj6m.fsf@evledraar.gmail.com","subject":"[PATCH 12/20] userdiff: match \"package\" in diff=golang","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T00:52:28Z","receivedAt":"2021-02-15T00:54:49Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Improve the \"golang\" built-in pattern to match \"package\" lines, as\nthey weren't matched before changing e.g. the imports would commonly\nresult in an empty hunk header, now we'll instead show the package\nname.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018/golang.sh | 10 ++++++++++\n userdiff.c        |  2 ++\n 2 files changed, 12 insertions(+)\n\ndiff --git a/t/t4018/golang.sh b/t/t4018/golang.sh\nindex bf22f58c12..cdf9d6f8aa 100755\n--- a/t/t4018/golang.sh\n+++ b/t/t4018/golang.sh\n@@ -3,6 +3,16 @@\n # See ../t4018-diff-funcname.sh's test_diff_funcname()\n #\n \n+test_diff_funcname 'golang: package' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+package main\n+EOF_HUNK\n+package main\n+\n+import \"fmt\"\n+// ChangeMe\n+EOF_TEST\n+\n test_diff_funcname 'golang: complex function' \\\n \t8<<\\EOF_HUNK 9<<\\EOF_TEST\n func (t *Test) RIGHT(a Type) (Type, error) {\ndiff --git a/userdiff.c b/userdiff.c\nindex 92b5a97e12..d99b488700 100644\n--- a/userdiff.c\n+++ b/userdiff.c\n@@ -124,6 +124,8 @@ IPATTERN(\"fortran\",\n IPATTERN(\"fountain\", \"^((\\\\.[^.]|(int|ext|est|int\\\\.?/ext|i/e)[. ]).*)$\",\n \t \"[^ \\t-]+\"),\n PATTERNS(\"golang\",\n+\t /* Packages */\n+\t \"^[ \\t]*(package[ \\t]*(.*))\\n\"\n \t /* Functions */\n \t \"^[ \\t]*(func[ \\t]*.*(\\\\{[ \\t]*)?)\\n\"\n \t /* Structs and interfaces */\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416900","messageId":"20210215005236.11313-14-avarab@gmail.com","threadId":"55144","inReplyTo":"87tuqebj6m.fsf@evledraar.gmail.com","subject":"[PATCH 13/20] userdiff tests + docs: document & test \"diff.<driver>.x?funcname\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T00:52:29Z","receivedAt":"2021-02-15T00:54:56Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add the missing documentation for \"diff.<driver>.funcname\" and test\nfor how it and \"diff.<driver>.xfuncname\" interact.\n\nBetween the introduction of the \"diff.<driver>.xfuncname\" form in\n45d9414fa5 (diff.*.xfuncname which uses \"extended\" regex's for hunk\nheader selection, 2008-09-18) and when this documentation was written\nin 90b94c26f7 (Documentation: Add diff.<driver>.* to config,\n2011-04-07) we forgot to document the existence of\n\"diff.<driver>.funcname\".\n\nLet's make a mention of it here, we could also partially revert the\nformer commit and discuss the more verbose form in gitattributes(5),\nbut let's stop short of that. It makes sense to guide users towards\nERE over BRE whenever possible.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/config/diff.txt | 12 +++++++++++\n t/t4018/custom.sh             | 40 +++++++++++++++++++++++++++++++++++\n 2 files changed, 52 insertions(+)\n\ndiff --git a/Documentation/config/diff.txt b/Documentation/config/diff.txt\nindex 2d3331f55c..5fce8021de 100644\n--- a/Documentation/config/diff.txt\n+++ b/Documentation/config/diff.txt\n@@ -153,10 +153,22 @@ diff.<driver>.command::\n \tThe custom diff driver command.  See linkgit:gitattributes[5]\n \tfor details.\n \n+diff.<driver>.funcname::\n diff.<driver>.xfuncname::\n \tThe regular expression that the diff driver should use to\n \trecognize the hunk header.  A built-in pattern may also be used.\n \tSee linkgit:gitattributes[5] for details.\n++\n+When provided as `diff.<driver>.funcname` the regular expression is\n+interpreted as a basic regular expression, with\n+`diff.<driver>.xfuncname` it's interpreted as an extended regular\n+expression.\n++\n+\n+The `*.funcname` and `*.xfuncname` variables behave as if though they\n+were one configuration variable for the purposes of what value\n+eventually gets used. Setting `*.funcname` will override an earlier\n+`*.xfuncname` and vice-versa.\n \n diff.<driver>.binary::\n \tSet this option to true to make the diff driver treat files as\ndiff --git a/t/t4018/custom.sh b/t/t4018/custom.sh\nindex 20abb38451..b68d96a8af 100755\n--- a/t/t4018/custom.sh\n+++ b/t/t4018/custom.sh\n@@ -78,6 +78,46 @@ public class Beer\n }\n EOF_TEST\n \n+test_expect_success 'custom; setup config precedence' '\n+\tgit config diff.custom.funcname \"foo\" &&\n+\tgit config diff.custom.xfuncname \"bar\"\n+'\n+\n+test_diff_funcname 'custom: config precedence' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+bar\n+EOF_HUNK\n+foo\n+bar\n+\n+ChangeMe\n+\n+baz\n+EOF_TEST\n+\n+test_expect_success 'custom: teardown' '\n+\ttest_unconfig diff.custom.funcname &&\n+\ttest_unconfig diff.custom.xfuncname\n+'\n+\n+test_expect_success 'custom; setup config precedence' '\n+\tgit config diff.custom.xfuncname \"bar\" &&\n+\tgit config diff.custom.funcname \"foo\"\n+\n+'\n+\n+test_diff_funcname 'custom: config precedence' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+foo\n+EOF_HUNK\n+foo\n+bar\n+\n+ChangeMe\n+\n+baz\n+EOF_TEST\n+\n test_expect_success 'custom: teardown' '\n \ttest_unconfig diff.custom.funcname &&\n \ttest_unconfig diff.custom.xfuncname\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416901","messageId":"20210215005236.11313-15-avarab@gmail.com","threadId":"55144","inReplyTo":"87tuqebj6m.fsf@evledraar.gmail.com","subject":"[PATCH 14/20] gitattributes doc: reword discussion of built-in userdiff patterns","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T00:52:30Z","receivedAt":"2021-02-15T00:55:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Reword the discussion of the built-in userdiff patterns to make it\nmore natural to precede it with a discussion about the semantics of\npattern matching, instead of assuming that it follows right after the\n\"diff.tex.xfuncname\" example which now immediately precedes it. This\nwill make a follow-up commit smaller.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/gitattributes.txt | 16 +++++++++++-----\n 1 file changed, 11 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\nindex e84e104f93..90992e2136 100644\n--- a/Documentation/gitattributes.txt\n+++ b/Documentation/gitattributes.txt\n@@ -794,11 +794,17 @@ backslashes; the pattern above picks a line that begins with a\n backslash, and zero or more occurrences of `sub` followed by\n `section` followed by open brace, to the end of line.\n \n-There are a few built-in patterns to make this easier, and `tex`\n-is one of them, so you do not have to write the above in your\n-configuration file (you still need to enable this with the\n-attribute mechanism, via `.gitattributes`).  The following built in\n-patterns are available:\n+There are built-in patterns shipped as part of git itself. A more\n+advanced version of the `tex` pattern discussed above is one of them.\n+\n+For built-in patterns you do not need the \"diff.tex.xfuncname\"\n+discussed above in your configuration file, but if present it'll\n+override the built-in pattern.\n+\n+You still need to enable built-in patterns with the the attribute\n+mechanism, via `.gitattributes`).\n+\n+The following built in patterns are available:\n \n - `ada` suitable for source code in the Ada language.\n \n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416902","messageId":"20210215005236.11313-18-avarab@gmail.com","threadId":"55144","inReplyTo":"87tuqebj6m.fsf@evledraar.gmail.com","subject":"[PATCH 17/20] userdiff tests: factor out test_diff_funcname() logic","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T00:52:33Z","receivedAt":"2021-02-15T00:55:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Factor out logic in test_diff_funcname() into two helper functions,\nthese will be useful in a follow-up commit where we'll do this munging\nin more than one place.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018-diff-funcname.sh | 16 +++++++++++++---\n 1 file changed, 13 insertions(+), 3 deletions(-)\n\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex 3e4c07e42b..7a830ec57f 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -47,6 +47,17 @@ test_expect_success 'last regexp must not be negated' '\n \ttest_i18ngrep \": Last expression must not be negated:\" msg\n '\n \n+do_change_me () {\n+\tfile=$1\n+\tsed -e \"s/ChangeMe/IWasChanged/\" <\"$file\" >tmp &&\n+\tmv tmp \"$file\"\n+}\n+\n+last_diff_context_line () {\n+\tfile=$1\n+\tsed -n -e \"s/^.*@@\\( \\|$\\)//p\" <$file\n+}\n+\n test_diff_funcname () {\n \tdesc=$1\n \tcat <&8 >arg.header &&\n@@ -57,13 +68,12 @@ test_diff_funcname () {\n \t\tcp arg.test \"$what\" &&\n \t\tcp arg.header expected &&\n \t\tgit add \"$what\" &&\n-\t\tsed -e \"s/ChangeMe/IWasChanged/\" <\"$what\" >tmp &&\n-\t\tmv tmp \"$what\"\n+\t\tdo_change_me \"$what\"\n \t' &&\n \n \ttest_expect_success \"$desc\" '\n \t\tgit diff -U1 \"$what\" >diff &&\n-\t\tsed -n -e \"s/^.*@@\\( \\|$\\)//p\" <diff >actual &&\n+\t\tlast_diff_context_line diff >actual &&\n \t\ttest_cmp expected actual\n \t'\n }\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416903","messageId":"20210215005236.11313-16-avarab@gmail.com","threadId":"55144","inReplyTo":"87tuqebj6m.fsf@evledraar.gmail.com","subject":"[PATCH 15/20] gitattributes doc: document multi-line userdiff patterns","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T00:52:31Z","receivedAt":"2021-02-15T00:55:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Document the multi-line userdiff patterns and how their matching and\nthe negation syntax works.\n\nThese patterns have been supported since f258475a6e (Per-path\nattribute based hunk header selection., 2007-07-06), and have had\ntheir current semantics ever since 3d8dccd74a (diff: fix \"multiple\nregexp\" semantics to find hunk header comment, 2008-09-20).\n\nBut we had no documentation for them, let's fix that, and also add\ntests showing how some of the things being discussed here work.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/gitattributes.txt | 17 ++++++++++++++++\n t/t4018/custom.sh               | 35 +++++++++++++++++++++++++++++++++\n 2 files changed, 52 insertions(+)\n\ndiff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\nindex 90992e2136..225c17b90d 100644\n--- a/Documentation/gitattributes.txt\n+++ b/Documentation/gitattributes.txt\n@@ -794,6 +794,23 @@ backslashes; the pattern above picks a line that begins with a\n backslash, and zero or more occurrences of `sub` followed by\n `section` followed by open brace, to the end of line.\n \n+Multiple patterns can be supplied by seperating them with\n+newlines. They will be matched one at a time and are compiled as\n+separate patterns, and thus the first capture in each such pattern is\n+`$1`, see further discussion of captures below.\n+\n+Patterns that begin with \"!\" are negated (to match a literal \"!\" at\n+the start of a line use e.g. \"[!]\"). A matching negated pattern will\n+cause the matching line to be skipped. Use it to blacklist otherwise\n+matching non-negated patterns. The last pattern must not be negated,\n+we'll error out if that's the case.\n+\n+If the pattern contains a `$1` capture it will be used instead of the\n+entire matching line (`$0`) to display the hunk header. This can be\n+used e.g. to strip whitespace from the beginning of the line, or to\n+only display the function name as part of a longer function\n+definition.\n+\n There are built-in patterns shipped as part of git itself. A more\n advanced version of the `tex` pattern discussed above is one of them.\n \ndiff --git a/t/t4018/custom.sh b/t/t4018/custom.sh\nindex b68d96a8af..cccf468c3a 100755\n--- a/t/t4018/custom.sh\n+++ b/t/t4018/custom.sh\n@@ -122,3 +122,38 @@ test_expect_success 'custom: teardown' '\n \ttest_unconfig diff.custom.funcname &&\n \ttest_unconfig diff.custom.xfuncname\n '\n+\n+test_expect_success 'custom: negation syntax, ! is magic' '\n+\tgit config diff.custom.xfuncname \"!negation\n+line\"\n+'\n+\n+test_diff_funcname 'custom: config precedence' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+line\n+EOF_HUNK\n+line\n+!negation\n+\n+ChangeMe\n+\n+baz\n+EOF_TEST\n+\n+test_expect_success 'custom: negation syntax, use [!] to override ! magic' '\n+\tgit config diff.custom.xfuncname \"[!]negation\n+line\"\n+'\n+\n+test_diff_funcname 'custom: config precedence' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+!negation\n+EOF_HUNK\n+line\n+!negation\n+\n+ChangeMe\n+\n+baz\n+EOF_TEST\n+\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416904","messageId":"20210215005236.11313-20-avarab@gmail.com","threadId":"55144","inReplyTo":"87tuqebj6m.fsf@evledraar.gmail.com","subject":"[PATCH 19/20] userdiff tests: test hunk header selection with -U0","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T00:52:35Z","receivedAt":"2021-02-15T00:55:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The userdiff tests have used a custom -U1 context since\nf12c66b9bb (userdiff/perl: anchor \"sub\" and \"package\" patterns on the\nleft, 2011-05-21). Changing it to -U0 doesn't change the results for\nany of the tests, except one.\n\nLet's test for this case explicitly. I.e. that we go \"beyond\" the\nselected context to find our hunk header. In many cases the desired\nhunk header is part of the diff itself under -U1.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018-diff-funcname.sh | 13 +++++++++++++\n t/t4018/custom.sh        |  1 +\n 2 files changed, 14 insertions(+)\n\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex 0d75d93c69..94026b8296 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -63,6 +63,7 @@ test_diff_funcname () {\n \tcat <&8 >arg.header &&\n \tcat <&9 >arg.test &&\n \twhat=$(cat arg.what) &&\n+\targ_diff_U0=$2 &&\n \n \ttest_expect_success \"setup: $desc\" '\n \t\tcp arg.test \"$what\" &&\n@@ -84,6 +85,18 @@ test_diff_funcname () {\n \t\ttest_cmp expected actual\n \t' &&\n \n+\ttest_expect_success \"$desc -U0\" '\n+\t\tgit diff -U0 \"$what\" >diff &&\n+\t\tlast_diff_context_line diff >actual &&\n+\t\tif test -n \"$arg_diff_U0\"\n+\t\tthen\n+\t\t\techo \"$arg_diff_U0\" >new-expected &&\n+\t\t\ttest_cmp new-expected actual\n+\t\telse\n+\t\t\ttest_cmp expected actual\n+\t\tfi\n+\t' &&\n+\n \ttest_expect_success \"$desc (accumulated)\" '\n \t\tgit diff -U1 \"$what\".acc >diff &&\n \t\tlast_diff_context_line diff >actual.lines &&\ndiff --git a/t/t4018/custom.sh b/t/t4018/custom.sh\nindex 81a68aa332..605e2d33ae 100755\n--- a/t/t4018/custom.sh\n+++ b/t/t4018/custom.sh\n@@ -10,6 +10,7 @@ test_expect_success 'custom: setup non-trivial custom' '\n '\n \n test_diff_funcname 'custom: non-trivial custom pattern' \\\n+\t'System.out.print(x + \" bottles of beer on the wall \"' \\\n \t8<<\\EOF_HUNK 9<<\\EOF_TEST\n int special, RIGHT;\n EOF_HUNK\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416905","messageId":"20210215005236.11313-17-avarab@gmail.com","threadId":"55144","inReplyTo":"87tuqebj6m.fsf@evledraar.gmail.com","subject":"[PATCH 16/20] userdiff tests: remove \"funcname\" from custom3 test","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T00:52:32Z","receivedAt":"2021-02-15T00:56:02Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"We can only have one \"funcname\" or \"xfuncname\", any later definition\noverrides the earlier one, so this configuration wasn't doing\nanything.\n\nWhen this test was originally added in 3632cfc248 (Use compatibility\nregex library for OSX/Darwin, 2008-09-07) we had no such definition of\ntwo patters for this test. Back then this was setting the\n\"diff.java.funcname\" configuration variable.\n\nThe stage for that second pattern being set got set later. In\n45d9414fa5 (diff.*.xfuncname which uses \"extended\" regex's for hunk\nheader selection, 2008-09-18) the pattern got converted from\n\"funcname\" to \"xfuncname\".\n\nSoon after in b19d288b4d (t4018-diff-funcname: demonstrate end of line\nfuncname matching flaw, 2008-10-15) another test immediately preceding\nthis one got added, using \"diff.java.funcname\" for its configuration.\n\nThen f792a0b88e (t4018 (funcname patterns): make configuration easier\nto track, 2011-05-21) came along and codified this whole thing when\nconverting the two tests from \"git config\" to \"test_config\".\n\nSince this was never the intent of the test let's just remove this,\nthe rationale in f792a0b88e for having some test for the clobbering\nbehavior makes sense, but I'll do that in another follow-up test, not\nas a hard to read side-effect of this one.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018/custom.sh | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/t/t4018/custom.sh b/t/t4018/custom.sh\nindex cccf468c3a..81a68aa332 100755\n--- a/t/t4018/custom.sh\n+++ b/t/t4018/custom.sh\n@@ -51,7 +51,6 @@ public class Beer\n EOF_TEST\n \n test_expect_success 'custom: setup alternation in pattern' '\n-\tgit config diff.custom.funcname \"Beer$\" &&\n \tgit config diff.custom.xfuncname \"^[ \t]*((public|static).*)$\"\n '\n \n@@ -133,6 +132,7 @@ test_diff_funcname 'custom: config precedence' \\\n line\n EOF_HUNK\n line\n+\n !negation\n \n ChangeMe\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416906","messageId":"20210215005236.11313-19-avarab@gmail.com","threadId":"55144","inReplyTo":"87tuqebj6m.fsf@evledraar.gmail.com","subject":"[PATCH 18/20] userdiff tests: test hunk headers on accumulated files","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T00:52:34Z","receivedAt":"2021-02-15T00:56:02Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The existing tests in \"t/t4018/\" are unrealistic in that they're all\nsetting up small few-line isolated test cases with one thing we could\nmatch as a hunk header, right above the one change in the file.\n\nExpand those tests by accumulating changes within the same file type\nin the \"test_diff_funcname\" function. So e.g. for \"bash\" we'll end up\na \"bash.acc\" file with 15 s/ChangeMe/IWasChanged/ changes.\n\nThis stress tests whether the hunk header selection will \"jump across\"\nto an earlier change because the match for that is greedier.\n\nAs it turns out we had one false positive in \"t/t4018/cpp.sh\" and\n\"t4018/matlab.sh\" because of how the tests were structured, we must\nalways give the \"ChangeMe\" line at least one line of separation from\nthe header, since it was at the end of those tests we'd select the\n\"wrong\" header. Let's adjust the spacing to compensate.\n\nSo in the end we found nothing of interest here, regardless, I think\nit is useful to continue to test in this mode. It's likely to aid in\nfinding bugs in combinations of our positive and negative matching as\nwe add more built-in patterns.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018-diff-funcname.sh | 19 +++++++++++++++++++\n t/t4018/cpp.sh           |  1 +\n t/t4018/matlab.sh        |  3 +++\n 3 files changed, 23 insertions(+)\n\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex 7a830ec57f..0d75d93c69 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -71,10 +71,24 @@ test_diff_funcname () {\n \t\tdo_change_me \"$what\"\n \t' &&\n \n+\ttest_expect_success \"setup: $desc (accumulated)\" '\n+\t\tcat arg.test >>arg.tests &&\n+\t\tcp arg.tests \"$what\".acc &&\n+\t\tgit add \"$what\".acc &&\n+\t\tdo_change_me \"$what\".acc\n+\t' &&\n+\n \ttest_expect_success \"$desc\" '\n \t\tgit diff -U1 \"$what\" >diff &&\n \t\tlast_diff_context_line diff >actual &&\n \t\ttest_cmp expected actual\n+\t' &&\n+\n+\ttest_expect_success \"$desc (accumulated)\" '\n+\t\tgit diff -U1 \"$what\".acc >diff &&\n+\t\tlast_diff_context_line diff >actual.lines &&\n+\t\ttail -n 1 actual.lines >actual &&\n+\t\ttest_cmp expected actual\n \t'\n }\n \n@@ -92,6 +106,11 @@ do\n \t\techo \"$what\" >arg.what\n \t' &&\n \n+\ttest_expect_success \"setup: hunk header for $what (accumulated)\" '\n+\t\t>arg.tests &&\n+\t\techo \"$what.acc diff=$what\" >>.gitattributes\n+\t' &&\n+\n \t. \"$test\"\n done\n \ndiff --git a/t/t4018/cpp.sh b/t/t4018/cpp.sh\nindex 185d40d5ef..e0ab749316 100755\n--- a/t/t4018/cpp.sh\n+++ b/t/t4018/cpp.sh\n@@ -206,6 +206,7 @@ void wrong()\n struct RIGHT_iterator_tag {};\n \n int ChangeMe;\n+\n EOF_TEST\n \n test_diff_funcname 'cpp: template function definition' \\\ndiff --git a/t/t4018/matlab.sh b/t/t4018/matlab.sh\nindex f62289148e..fba410e6f5 100755\n--- a/t/t4018/matlab.sh\n+++ b/t/t4018/matlab.sh\n@@ -31,6 +31,7 @@ EOF_HUNK\n %%% RIGHT section\n # this is octave script\n ChangeMe = 1;\n+\n EOF_TEST\n \n test_diff_funcname 'matlab: octave section 2' \\\n@@ -40,6 +41,7 @@ EOF_HUNK\n ## RIGHT section\n # this is octave script\n ChangeMe = 1;\n+\n EOF_TEST\n \n test_diff_funcname 'matlab: section' \\\n@@ -49,4 +51,5 @@ EOF_HUNK\n %% RIGHT section\n % this is understood by both matlab and octave\n ChangeMe = 1;\n+\n EOF_TEST\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416907","messageId":"20210215005236.11313-21-avarab@gmail.com","threadId":"55144","inReplyTo":"87tuqebj6m.fsf@evledraar.gmail.com","subject":"[PATCH 20/20] userdiff tests: assert empty hunk header context on -U<large>","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T00:52:36Z","receivedAt":"2021-02-15T00:56:08Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Assert the existing behavior that under -U<large> we'll show no hunk\nheader context, where <large> takes us past the potential hunk header\nwe'd have extracted. I'm just picking a number over nine thousand as a\nreally large number we're unlikely to exceed in these tests.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018-diff-funcname.sh | 8 ++++++++\n 1 file changed, 8 insertions(+)\n\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex 94026b8296..bd81974dab 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -103,6 +103,14 @@ test_diff_funcname () {\n \t\ttail -n 1 actual.lines >actual &&\n \t\ttest_cmp expected actual\n \t'\n+\n+\ttest_expect_success \"$desc -U9001 (accumulated)\" '\n+\t\tgit diff -U9001 \"$what\".acc >diff &&\n+\t\tlast_diff_context_line diff >actual.lines &&\n+\t\ttail -n 1 actual.lines >actual &&\n+\t\techo >blank &&\n+\t\ttest_cmp blank actual\n+\t'\n }\n \n for what in $diffpatterns\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416908","messageId":"CAPig+cQbPkfh7f2VpeCitRwRMothYqUvUqQk0HiZnUGUpvWTvw@mail.gmail.com","threadId":"55144","inReplyTo":"20210215005236.11313-6-avarab@gmail.com","subject":"Re: [PATCH 05/20] userdiff tests: list builtin drivers via test-tool","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-02-15T01:24:13Z","receivedAt":"2021-02-15T01:25:23Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sun, Feb 14, 2021 at 7:56 PM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n> Change the userdiff test to list the builtin drivers via the\n> test-tool, using the new for_each_userdiff_driver() API function.\n> [...]\n> I only need the \"list-builtin-drivers \"argument here, but let's add\n> \"list-custom-drivers\" and \"list-drivers\" too, just because it's easy.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n> diff --git a/t/helper/test-userdiff.c b/t/helper/test-userdiff.c\n> @@ -0,0 +1,32 @@\n> +static int userdiff_list_builtin_drivers_cb(struct userdiff_driver *driver,\n> +                                           enum userdiff_driver_type type,\n> +                                           void *priv)\n> +{\n> +       puts(driver->name);\n> +       return 0;\n> +}\n\nNit: The word \"builtin\" in the name of this callback function made me\nsearch the patch for its cousin function which would be used for\ncustom drivers. It was only after reading on that I discovered that\nthis one function is used for both builtin and custom drivers. (Caught\nme off guard, but not itself worth a re-roll.)\n\n> diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\n> @@ -8,6 +8,10 @@ test_description='Test custom diff function name patterns'\n>  test_expect_success 'setup' '\n> +       test-tool userdiff list-builtin-drivers >builtin-drivers &&\n> +       test_file_not_empty builtin-drivers &&\n> +       builtin_drivers=$(cat builtin-drivers) &&\n\nNit: I get why you are using test_file_not_empty() for its diagnostic\nvalue, but it does confuse the reader a bit to see the file\n\"builtin-drivers\" created for no particularly good reason. This could\neasily have been:\n\n    builtin_drivers=$(test-tool userdiff list-builtin-drivers) &&\n    test -n \"$builtin_drivers\" &&\n\nSubjective and not worth a re-roll.\n"},{"id":"416909","messageId":"CAPig+cSo-a12aUo-Xwo=7nECpXTsWv+nsoh6DnfUQGk4TZeqyw@mail.gmail.com","threadId":"55144","inReplyTo":"20210215005236.11313-8-avarab@gmail.com","subject":"Re: [PATCH 07/20] userdiff tests: match full hunk headers","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-02-15T01:35:26Z","receivedAt":"2021-02-15T01:36:37Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sun, Feb 14, 2021 at 7:56 PM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n> [...]\n> Let's bring back coverage for that by adding corresponding *.ctx\n> files, this has the added advantage that we're doing a \"test_cmp\", so\n> when we have failures it's just a non-zero exit code from \"grep\",\n> we'll actually have something meaningful in the \"-v\" output.\n> [...]\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n> diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\n> @@ -81,11 +81,12 @@ test_expect_success 'setup hunk header tests' '\n> +for i in $(git ls-files -- ':!*.ctx')\n>  do\n>         test_expect_success \"hunk header: $i\" \"\n> +               git diff -U1 $i >diff &&\n> +               sed -n -e 's/^.*@@\\( \\|$\\)//p' <diff >ctx &&\n> +               test_cmp $i.ctx ctx\n>         \"\n>  done\n\nIf I'm reading this correctly, you're simply stripping off all the\nleading `@@ blah @@` stuff...\n\n> diff --git a/t/t4018/README b/t/t4018/README\n> @@ -1,15 +1,15 @@\n> +The text that must appear in the hunk header must contains the word\n> +\"RIGHT\" by convention. The \"LANG-whatever.ctx\" file contains what we\n> +expect to appear in the hunk header. We munged the start of the line\n> +to \"@@ [...] @@\" for ease of not having to hardcode the line numbers\n> +and offsets.\n\n...which makes me wonder what this \"munging to `@@ [...] @@`\" is about.\n\nIs this documentation update wrong or am I misunderstanding?\n"},{"id":"416910","messageId":"CAPx1GvfFPWvJsj+uJV7RZrv1rgEpio=pk6rKF2UrjHebVY=LPA@mail.gmail.com","threadId":"55144","inReplyTo":"20210215005236.11313-16-avarab@gmail.com","subject":"Re: [PATCH 15/20] gitattributes doc: document multi-line userdiff patterns","fromName":"Chris Torek","fromEmail":"chris.torek@gmail.com","sentAt":"2021-02-15T03:16:01Z","receivedAt":"2021-02-15T03:17:13Z","isPatch":true,"sender":{"key":"chris.torek@gmail.com","avatar":"https://avatars.githubusercontent.com/u/16826774?v=4"},"body":"This is extremely trivial, just something to consider if you are\nalready doing a reroll:\n\nOn Sun, Feb 14, 2021 at 4:57 PM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n> diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\n> index 90992e2136..225c17b90d 100644\n> --- a/Documentation/gitattributes.txt\n> +++ b/Documentation/gitattributes.txt\n> @@ -794,6 +794,23 @@ backslashes; the pattern above picks a line that begins with a\n>  backslash, and zero or more occurrences of `sub` followed by\n>  `section` followed by open brace, to the end of line.\n>\n> +Multiple patterns can be supplied by seperating them with\n> +newlines. They will be matched one at a time and are compiled as\n> +separate patterns, and thus the first capture in each such pattern is\n> +`$1`, see further discussion of captures below.\n\nThis is a comma splice (https://en.wikipedia.org/wiki/Comma_splice).\nUse a period or semicolon to fix it.\n\n(overall comment, haven't quite finished scanning the series yet but it\nlooks good!)\n\nChris\n"},{"id":"416911","messageId":"CAPig+cRmfeW=Di5uS9LnE0EVFnm5A5j=-z46J-zYGMm1E-8K6w@mail.gmail.com","threadId":"55144","inReplyTo":"20210215005236.11313-14-avarab@gmail.com","subject":"Re: [PATCH 13/20] userdiff tests + docs: document & test \"diff.<driver>.x?funcname\"","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-02-15T03:16:28Z","receivedAt":"2021-02-15T03:17:37Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sun, Feb 14, 2021 at 7:56 PM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n> Add the missing documentation for \"diff.<driver>.funcname\" and test\n> for how it and \"diff.<driver>.xfuncname\" interact.\n> [...]\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n> diff --git a/Documentation/config/diff.txt b/Documentation/config/diff.txt\n> @@ -153,10 +153,22 @@ diff.<driver>.command::\n> +diff.<driver>.funcname::\n>  diff.<driver>.xfuncname::\n>         The regular expression that the diff driver should use to\n>         recognize the hunk header.  A built-in pattern may also be used.\n>         See linkgit:gitattributes[5] for details.\n> ++\n> +When provided as `diff.<driver>.funcname` the regular expression is\n> +interpreted as a basic regular expression, with\n\nThis would be easier to understand: s/, with/. With/\n\n> +`diff.<driver>.xfuncname` it's interpreted as an extended regular\n> +expression.\n> ++\n> +\n> +The `*.funcname` and `*.xfuncname` variables behave as if though they\n> +were one configuration variable for the purposes of what value\n> +eventually gets used. Setting `*.funcname` will override an earlier\n> +`*.xfuncname` and vice-versa.\n\nExtra blank line before this paragraph?\n"},{"id":"416912","messageId":"CAPig+cThGFFzNFxVo-Vdoq2HzonndrvHPjbCUznuBbMGvvLuAQ@mail.gmail.com","threadId":"55144","inReplyTo":"20210215005236.11313-15-avarab@gmail.com","subject":"Re: [PATCH 14/20] gitattributes doc: reword discussion of built-in userdiff patterns","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-02-15T03:26:14Z","receivedAt":"2021-02-15T03:27:17Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sun, Feb 14, 2021 at 7:56 PM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n> Reword the discussion of the built-in userdiff patterns to make it\n> more natural to precede it with a discussion about the semantics of\n> pattern matching, instead of assuming that it follows right after the\n> \"diff.tex.xfuncname\" example which now immediately precedes it. This\n> will make a follow-up commit smaller.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n> diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\n> @@ -794,11 +794,17 @@ backslashes; the pattern above picks a line that begins with a\n> -There are a few built-in patterns to make this easier, and `tex`\n> -is one of them, so you do not have to write the above in your\n> -configuration file (you still need to enable this with the\n> -attribute mechanism, via `.gitattributes`).  The following built in\n> -patterns are available:\n> +There are built-in patterns shipped as part of git itself. A more\n> +advanced version of the `tex` pattern discussed above is one of them.\n> +\n> +For built-in patterns you do not need the \"diff.tex.xfuncname\"\n> +discussed above in your configuration file, but if present it'll\n> +override the built-in pattern.\n\nThe literal \"diff.tex.xfuncname\" now feels disconnected from what is\nbeing said, especially as it is preceded by a reference to generic\n\"built-in patterns\". Perhaps it would make sense to generalize it a\nbit to `diff.<lang>.xfuncname`. For instance:\n\n    For built-in patterns, you do not need `diff.<lang>.xfuncname`\n    in your configuration file as discussed above, but if present,\n    it will override a built-in pattern.\n\n> +You still need to enable built-in patterns with the the attribute\n> +mechanism, via `.gitattributes`).\n\ns/the the/the/\n\nAlthough you're simply re-using the existing text, it also now feels\ndisconnected. Perhaps:\n\n    Nevertheless, you need to enable built-in patterns via\n    `.gitattributes` for the pattern to take effect.\n"},{"id":"416913","messageId":"CAPig+cTUAWoDMf-SYhBZiptccX8hSAsT+oODxSwH5jxws+_p4g@mail.gmail.com","threadId":"55144","inReplyTo":"20210215005236.11313-16-avarab@gmail.com","subject":"Re: [PATCH 15/20] gitattributes doc: document multi-line userdiff patterns","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-02-15T03:36:50Z","receivedAt":"2021-02-15T03:38:00Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sun, Feb 14, 2021 at 7:56 PM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n> Document the multi-line userdiff patterns and how their matching and\n> the negation syntax works.\n> [...]\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n> diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\n> @@ -794,6 +794,23 @@ backslashes; the pattern above picks a line that begins with a\n> +Multiple patterns can be supplied by seperating them with\n\ns/seperating/separating/\n\n> +newlines. They will be matched one at a time and are compiled as\n> +separate patterns, and thus the first capture in each such pattern is\n> +`$1`, see further discussion of captures below.\n\nI found the wording \"separating them with newlines\" ambiguous. I\ncouldn't figure out if that meant that there must be a blank line\nbetween patterns. Would it be more accurate to say merely that the\npatterns must be listed one per line?\n\n> +Patterns that begin with \"!\" are negated (to match a literal \"!\" at\n> +the start of a line use e.g. \"[!]\"). A matching negated pattern will\n> +cause the matching line to be skipped. Use it to blacklist otherwise\n> +matching non-negated patterns. The last pattern must not be negated,\n> +we'll error out if that's the case.\n\nThe parenthesized comment makes it difficult to follow the discussion.\nMoving the comment to the end of the paragraph would make it easier to\ngrok:\n\n    Patterns that begin with \"!\" are negated. A matching...\n    ...error out if that's the case. To match a literal \"!\" at\n    the start of a line, use \"[!]\".\n\nI think, also, you want s/matching line/matched line/.\n\nChris's comma-splice comment also seems applicable for the last\nsentence in this paragraph.\n"},{"id":"416915","messageId":"20210215154427.32693-3-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215005236.11313-1-avarab@gmail.com","subject":"[PATCH v2 02/27] userdiff style: re-order drivers in alphabetical order","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T15:44:02Z","receivedAt":"2021-02-15T15:47:24Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Address some old code smell and move around the built-in userdiff\ndrivers so they're both in alphabetical order, and now in the same\norder they appear in the gitattributes(5) documentation.\n\nThe two started drifting in be58e70dba (diff: unify external diff and\nfuncname parsing code, 2008-10-05), and then even further in\n80c49c3de2 (color-words: make regex configurable via attributes,\n2009-01-17) when the \"cpp\" pattern was added.\n\nThere are no functional changes here, and as --color-moved will show\nonly moved existing lines.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n userdiff.c | 76 +++++++++++++++++++++++++++---------------------------\n 1 file changed, 38 insertions(+), 38 deletions(-)\n\ndiff --git a/userdiff.c b/userdiff.c\nindex c147bcbb17..c92cbcc054 100644\n--- a/userdiff.c\n+++ b/userdiff.c\n@@ -44,6 +44,44 @@ PATTERNS(\"bash\",\n \t /* -- */\n \t /* Characters not in the default $IFS value */\n \t \"[^ \\t]+\"),\n+PATTERNS(\"bibtex\", \"(@[a-zA-Z]{1,}[ \\t]*\\\\{{0,1}[ \\t]*[^ \\t\\\"@',\\\\#}{~%]*).*$\",\n+\t \"[={}\\\"]|[^={}\\\" \\t]+\"),\n+PATTERNS(\"cpp\",\n+\t /* Jump targets or access declarations */\n+\t \"!^[ \\t]*[A-Za-z_][A-Za-z_0-9]*:[[:space:]]*($|/[/*])\\n\"\n+\t /* functions/methods, variables, and compounds at top level */\n+\t \"^((::[[:space:]]*)?[A-Za-z_].*)$\",\n+\t /* -- */\n+\t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n+\t \"|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lLuU]*\"\n+\t \"|[-+*/<>%&^|=!]=|--|\\\\+\\\\+|<<=?|>>=?|&&|\\\\|\\\\||::|->\\\\*?|\\\\.\\\\*\"),\n+PATTERNS(\"csharp\",\n+\t /* Keywords */\n+\t \"!^[ \\t]*(do|while|for|if|else|instanceof|new|return|switch|case|throw|catch|using)\\n\"\n+\t /* Methods and constructors */\n+\t \"^[ \\t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe|async)[ \\t]+)*[][<>@.~_[:alnum:]]+[ \\t]+[<>@._[:alnum:]]+[ \\t]*\\\\(.*\\\\))[ \\t]*$\\n\"\n+\t /* Properties */\n+\t \"^[ \\t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \\t]+)*[][<>@.~_[:alnum:]]+[ \\t]+[@._[:alnum:]]+)[ \\t]*$\\n\"\n+\t /* Type definitions */\n+\t \"^[ \\t]*(((static|public|internal|private|protected|new|unsafe|sealed|abstract|partial)[ \\t]+)*(class|enum|interface|struct)[ \\t]+.*)$\\n\"\n+\t /* Namespace */\n+\t \"^[ \\t]*(namespace[ \\t]+.*)$\",\n+\t /* -- */\n+\t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n+\t \"|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?\"\n+\t \"|[-+*/<>%&^|=!]=|--|\\\\+\\\\+|<<=?|>>=?|&&|\\\\|\\\\||::|->\"),\n+IPATTERN(\"css\",\n+\t \"![:;][[:space:]]*$\\n\"\n+\t \"^[:[@.#]?[_a-z0-9].*$\",\n+\t /* -- */\n+\t /*\n+\t  * This regex comes from W3C CSS specs. Should theoretically also\n+\t  * allow ISO 10646 characters U+00A0 and higher,\n+\t  * but they are not handled in this regex.\n+\t  */\n+\t \"-?[_a-zA-Z][-_a-zA-Z0-9]*\" /* identifiers */\n+\t \"|-?[0-9]+|\\\\#[0-9a-fA-F]+\" /* numbers */\n+),\n PATTERNS(\"dts\",\n \t \"!;\\n\"\n \t \"!=\\n\"\n@@ -191,46 +229,8 @@ PATTERNS(\"rust\",\n \t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n \t \"|[0-9][0-9_a-fA-Fiosuxz]*(\\\\.([0-9]*[eE][+-]?)?[0-9_fF]*)?\"\n \t \"|[-+*\\\\/<>%&^|=!:]=|<<=?|>>=?|&&|\\\\|\\\\||->|=>|\\\\.{2}=|\\\\.{3}|::\"),\n-PATTERNS(\"bibtex\", \"(@[a-zA-Z]{1,}[ \\t]*\\\\{{0,1}[ \\t]*[^ \\t\\\"@',\\\\#}{~%]*).*$\",\n-\t \"[={}\\\"]|[^={}\\\" \\t]+\"),\n PATTERNS(\"tex\", \"^(\\\\\\\\((sub)*section|chapter|part)\\\\*{0,1}\\\\{.*)$\",\n \t \"\\\\\\\\[a-zA-Z@]+|\\\\\\\\.|[a-zA-Z0-9\\x80-\\xff]+\"),\n-PATTERNS(\"cpp\",\n-\t /* Jump targets or access declarations */\n-\t \"!^[ \\t]*[A-Za-z_][A-Za-z_0-9]*:[[:space:]]*($|/[/*])\\n\"\n-\t /* functions/methods, variables, and compounds at top level */\n-\t \"^((::[[:space:]]*)?[A-Za-z_].*)$\",\n-\t /* -- */\n-\t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n-\t \"|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lLuU]*\"\n-\t \"|[-+*/<>%&^|=!]=|--|\\\\+\\\\+|<<=?|>>=?|&&|\\\\|\\\\||::|->\\\\*?|\\\\.\\\\*\"),\n-PATTERNS(\"csharp\",\n-\t /* Keywords */\n-\t \"!^[ \\t]*(do|while|for|if|else|instanceof|new|return|switch|case|throw|catch|using)\\n\"\n-\t /* Methods and constructors */\n-\t \"^[ \\t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe|async)[ \\t]+)*[][<>@.~_[:alnum:]]+[ \\t]+[<>@._[:alnum:]]+[ \\t]*\\\\(.*\\\\))[ \\t]*$\\n\"\n-\t /* Properties */\n-\t \"^[ \\t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \\t]+)*[][<>@.~_[:alnum:]]+[ \\t]+[@._[:alnum:]]+)[ \\t]*$\\n\"\n-\t /* Type definitions */\n-\t \"^[ \\t]*(((static|public|internal|private|protected|new|unsafe|sealed|abstract|partial)[ \\t]+)*(class|enum|interface|struct)[ \\t]+.*)$\\n\"\n-\t /* Namespace */\n-\t \"^[ \\t]*(namespace[ \\t]+.*)$\",\n-\t /* -- */\n-\t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n-\t \"|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?\"\n-\t \"|[-+*/<>%&^|=!]=|--|\\\\+\\\\+|<<=?|>>=?|&&|\\\\|\\\\||::|->\"),\n-IPATTERN(\"css\",\n-\t \"![:;][[:space:]]*$\\n\"\n-\t \"^[:[@.#]?[_a-z0-9].*$\",\n-\t /* -- */\n-\t /*\n-\t  * This regex comes from W3C CSS specs. Should theoretically also\n-\t  * allow ISO 10646 characters U+00A0 and higher,\n-\t  * but they are not handled in this regex.\n-\t  */\n-\t \"-?[_a-zA-Z][-_a-zA-Z0-9]*\" /* identifiers */\n-\t \"|-?[0-9]+|\\\\#[0-9a-fA-F]+\" /* numbers */\n-),\n { \"default\", NULL, -1, { NULL, 0 } },\n };\n #undef PATTERNS\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416916","messageId":"20210215154427.32693-2-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215005236.11313-1-avarab@gmail.com","subject":"[PATCH v2 01/27] userdiff: refactor away the parse_bool() function","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T15:44:01Z","receivedAt":"2021-02-15T15:48:14Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Since 6680a0874f (drop odd return value semantics from\nuserdiff_config, 2012-02-07) we have not cared about the return values\nof parse_tristate() or git_config_bool() v.s. falling through in\nuserdiff_config(), so let's do so in those cases to make the code\neasier to read.\n\nHaving a wrapper function for git_config_bool() dates back to\nd9bae1a178 (diff: cache textconv output, 2010-04-01) and\n122aa6f9c0 (diff: introduce diff.<driver>.binary, 2008-10-05), both of\nwhich predated the change in 6680a0874f which made their return values\nredundant.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n userdiff.c | 18 ++++++------------\n 1 file changed, 6 insertions(+), 12 deletions(-)\n\ndiff --git a/userdiff.c b/userdiff.c\nindex 3f81a2261c..c147bcbb17 100644\n--- a/userdiff.c\n+++ b/userdiff.c\n@@ -275,19 +275,12 @@ static int parse_funcname(struct userdiff_funcname *f, const char *k,\n \treturn 0;\n }\n \n-static int parse_tristate(int *b, const char *k, const char *v)\n+static void parse_tristate(int *b, const char *k, const char *v)\n {\n \tif (v && !strcasecmp(v, \"auto\"))\n \t\t*b = -1;\n \telse\n \t\t*b = git_config_bool(k, v);\n-\treturn 0;\n-}\n-\n-static int parse_bool(int *b, const char *k, const char *v)\n-{\n-\t*b = git_config_bool(k, v);\n-\treturn 0;\n }\n \n int userdiff_config(const char *k, const char *v)\n@@ -312,16 +305,17 @@ int userdiff_config(const char *k, const char *v)\n \t\treturn parse_funcname(&drv->funcname, k, v, 0);\n \tif (!strcmp(type, \"xfuncname\"))\n \t\treturn parse_funcname(&drv->funcname, k, v, REG_EXTENDED);\n-\tif (!strcmp(type, \"binary\"))\n-\t\treturn parse_tristate(&drv->binary, k, v);\n \tif (!strcmp(type, \"command\"))\n \t\treturn git_config_string(&drv->external, k, v);\n \tif (!strcmp(type, \"textconv\"))\n \t\treturn git_config_string(&drv->textconv, k, v);\n-\tif (!strcmp(type, \"cachetextconv\"))\n-\t\treturn parse_bool(&drv->textconv_want_cache, k, v);\n \tif (!strcmp(type, \"wordregex\"))\n \t\treturn git_config_string(&drv->word_regex, k, v);\n+\t/* Don't care about the parse errors for these, fallthrough */\n+\tif (!strcmp(type, \"cachetextconv\"))\n+\t\tdrv->textconv_want_cache = git_config_bool(k, v);\n+\tif (!strcmp(type, \"binary\"))\n+\t\tparse_tristate(&drv->binary, k, v);\n \n \treturn 0;\n }\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416917","messageId":"20210215154427.32693-1-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215005236.11313-1-avarab@gmail.com","subject":"[PATCH v2 00/27] userdiff: refactor + test + doc + misc improvements","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T15:44:00Z","receivedAt":"2021-02-15T15:48:14Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Incorporates all the feedback on v2 and more, see the range-diff\nbelow.\n\nÆvar Arnfjörð Bjarmason (27):\n  userdiff: refactor away the parse_bool() function\n  userdiff style: re-order drivers in alphabetical order\n  userdiff style: declare patterns with consistent style\n  userdiff style: normalize pascal regex declaration\n  userdiff: add and use for_each_userdiff_driver()\n  userdiff tests: explicitly test \"default\" pattern\n  userdiff tests: list builtin drivers via test-tool\n  userdiff: remove support for \"broken\" tests\n  userdiff tests: match full hunk headers\n  blame tests: don't rely on t/t4018/ directory\n  blame tests: simplify userdiff driver test\n  userdiff tests: rewrite hunk header test infrastructure\n  userdiff tests: do config teardown in test_diff_funcname()\n  userdiff tests: move custom patterns into one test file\n  userdiff tests: remove hack for \"RIGHT\" token\n  userdiff tests: do not do compile tests on \"custom\" pattern\n  userdiff tests + docs: document & test \"diff.<driver>.x?funcname\"\n  gitattributes doc: reword discussion of built-in userdiff patterns\n  gitattributes doc: document multi-line userdiff patterns\n  userdiff tests: remove \"funcname\" from custom3 test\n  userdiff tests: factor out test_diff_funcname() logic\n  userdiff tests: test hunk headers on accumulated files\n  userdiff tests: test hunk header selection with -U0\n  userdiff tests: assert empty hunk header context on -U<large>\n  userdiff: match \"package\" in diff=golang\n  userdiff tests: add basic test for ada\n  userdiff tests: add basic test for ruby\n\n Documentation/config/diff.txt              |  11 +\n Documentation/gitattributes.txt            |  41 +++-\n Makefile                                   |   1 +\n t/annotate-tests.sh                        |  34 +--\n t/helper/test-tool.c                       |   1 +\n t/helper/test-tool.h                       |   1 +\n t/helper/test-userdiff.c                   |  30 +++\n t/t4018-diff-funcname.sh                   | 163 ++++++++------\n t/t4018/README                             |  18 --\n t/t4018/ada.sh                             |  37 ++++\n t/t4018/bash-arithmetic-function           |   4 -\n t/t4018/bash-bashism-style-compact         |   6 -\n t/t4018/bash-bashism-style-function        |   4 -\n t/t4018/bash-bashism-style-whitespace      |   4 -\n t/t4018/bash-conditional-function          |   4 -\n t/t4018/bash-missing-parentheses           |   6 -\n t/t4018/bash-mixed-style-compact           |   4 -\n t/t4018/bash-mixed-style-function          |   4 -\n t/t4018/bash-nested-functions              |   6 -\n t/t4018/bash-other-characters              |   4 -\n t/t4018/bash-posix-style-compact           |   4 -\n t/t4018/bash-posix-style-function          |   4 -\n t/t4018/bash-posix-style-whitespace        |   4 -\n t/t4018/bash-subshell-function             |   4 -\n t/t4018/bash-trailing-comment              |   4 -\n t/t4018/bash.sh                            | 160 ++++++++++++++\n t/t4018/cpp-c++-function                   |   4 -\n t/t4018/cpp-class-constructor              |   4 -\n t/t4018/cpp-class-constructor-mem-init     |   5 -\n t/t4018/cpp-class-definition               |   4 -\n t/t4018/cpp-class-definition-derived       |   5 -\n t/t4018/cpp-class-destructor               |   4 -\n t/t4018/cpp-function-returning-global-type |   4 -\n t/t4018/cpp-function-returning-nested      |   5 -\n t/t4018/cpp-function-returning-pointer     |   4 -\n t/t4018/cpp-function-returning-reference   |   4 -\n t/t4018/cpp-gnu-style-function             |   5 -\n t/t4018/cpp-namespace-definition           |   4 -\n t/t4018/cpp-operator-definition            |   4 -\n t/t4018/cpp-skip-access-specifiers         |   8 -\n t/t4018/cpp-skip-comment-block             |   9 -\n t/t4018/cpp-skip-labels                    |   8 -\n t/t4018/cpp-struct-definition              |   9 -\n t/t4018/cpp-struct-single-line             |   7 -\n t/t4018/cpp-template-function-definition   |   4 -\n t/t4018/cpp-union-definition               |   4 -\n t/t4018/cpp-void-c-function                |   4 -\n t/t4018/cpp.sh                             | 240 +++++++++++++++++++++\n t/t4018/css-attribute-value-selector       |   4 -\n t/t4018/css-block-level-@-statements       |  10 -\n t/t4018/css-brace-in-col-1                 |   5 -\n t/t4018/css-class-selector                 |   4 -\n t/t4018/css-colon-eol                      |   4 -\n t/t4018/css-colon-selector                 |   5 -\n t/t4018/css-common                         |   4 -\n t/t4018/css-id-selector                    |   4 -\n t/t4018/css-long-selector-list             |   6 -\n t/t4018/css-prop-sans-indent               |   5 -\n t/t4018/css-root-selector                  |   4 -\n t/t4018/css-short-selector-list            |   4 -\n t/t4018/css-trailing-space                 |   5 -\n t/t4018/css.sh                             | 146 +++++++++++++\n t/t4018/custom.sh                          | 163 ++++++++++++++\n t/t4018/custom1-pattern                    |  17 --\n t/t4018/custom2-match-to-end-of-line       |   8 -\n t/t4018/custom3-alternation-in-pattern     |  17 --\n t/t4018/dts-labels                         |   9 -\n t/t4018/dts-node-unitless                  |   8 -\n t/t4018/dts-nodes                          |   8 -\n t/t4018/dts-nodes-boolean-prop             |   9 -\n t/t4018/dts-nodes-comment1                 |   8 -\n t/t4018/dts-nodes-comment2                 |   8 -\n t/t4018/dts-nodes-multiline-prop           |  13 --\n t/t4018/dts-reference                      |   9 -\n t/t4018/dts-root                           |   5 -\n t/t4018/dts-root-comment                   |   8 -\n t/t4018/dts.sh                             | 149 +++++++++++++\n t/t4018/elixir-do-not-pick-end             |   5 -\n t/t4018/elixir-ex-unit-test                |   6 -\n t/t4018/elixir-function                    |   5 -\n t/t4018/elixir-macro                       |   5 -\n t/t4018/elixir-module                      |   9 -\n t/t4018/elixir-module-func                 |   8 -\n t/t4018/elixir-nested-module               |   9 -\n t/t4018/elixir-private-function            |   5 -\n t/t4018/elixir-protocol                    |   6 -\n t/t4018/elixir-protocol-implementation     |   5 -\n t/t4018/elixir.sh                          | 127 +++++++++++\n t/t4018/fortran-block-data                 |   5 -\n t/t4018/fortran-comment                    |  13 --\n t/t4018/fortran-comment-keyword            |  14 --\n t/t4018/fortran-comment-legacy             |  13 --\n t/t4018/fortran-comment-legacy-star        |  13 --\n t/t4018/fortran-external-function          |   9 -\n t/t4018/fortran-external-subroutine        |   5 -\n t/t4018/fortran-module                     |   5 -\n t/t4018/fortran-module-procedure           |  13 --\n t/t4018/fortran-program                    |   5 -\n t/t4018/fortran.sh                         | 159 ++++++++++++++\n t/t4018/fountain-scene                     |   4 -\n t/t4018/fountain.sh                        |  14 ++\n t/t4018/golang-complex-function            |   8 -\n t/t4018/golang-func                        |   4 -\n t/t4018/golang-interface                   |   4 -\n t/t4018/golang-long-func                   |   5 -\n t/t4018/golang-struct                      |   4 -\n t/t4018/golang.sh                          |  69 ++++++\n t/t4018/java-class-member-function         |   8 -\n t/t4018/java.sh                            |  18 ++\n t/t4018/markdown-heading-indented          |   6 -\n t/t4018/markdown-heading-non-headings      |  17 --\n t/t4018/markdown.sh                        |  39 ++++\n t/t4018/matlab-class-definition            |   5 -\n t/t4018/matlab-function                    |   4 -\n t/t4018/matlab-octave-section-1            |   3 -\n t/t4018/matlab-octave-section-2            |   3 -\n t/t4018/matlab-section                     |   3 -\n t/t4018/matlab.sh                          |  55 +++++\n t/t4018/perl-skip-end-of-heredoc           |   8 -\n t/t4018/perl-skip-forward-decl             |  10 -\n t/t4018/perl-skip-sub-in-pod               |  18 --\n t/t4018/perl-sub-definition                |   4 -\n t/t4018/perl-sub-definition-kr-brace       |   4 -\n t/t4018/perl.sh                            |  94 ++++++++\n t/t4018/php-abstract-class                 |   4 -\n t/t4018/php-abstract-method                |   7 -\n t/t4018/php-class                          |   4 -\n t/t4018/php-final-class                    |   4 -\n t/t4018/php-final-method                   |   7 -\n t/t4018/php-function                       |   4 -\n t/t4018/php-interface                      |   4 -\n t/t4018/php-method                         |   7 -\n t/t4018/php-trait                          |   7 -\n t/t4018/php.sh                             | 106 +++++++++\n t/t4018/python-async-def                   |   4 -\n t/t4018/python-class                       |   4 -\n t/t4018/python-def                         |   4 -\n t/t4018/python-indented-async-def          |   7 -\n t/t4018/python-indented-class              |   5 -\n t/t4018/python-indented-def                |   7 -\n t/t4018/python.sh                          |  71 ++++++\n t/t4018/ruby.sh                            |  58 +++++\n t/t4018/rust-fn                            |   5 -\n t/t4018/rust-impl                          |   5 -\n t/t4018/rust-macro-rules                   |   6 -\n t/t4018/rust-struct                        |   5 -\n t/t4018/rust-trait                         |   5 -\n t/t4018/rust.sh                            |  60 ++++++\n userdiff.c                                 | 180 ++++++++++------\n userdiff.h                                 |  15 ++\n 150 files changed, 2083 insertions(+), 928 deletions(-)\n create mode 100644 t/helper/test-userdiff.c\n delete mode 100644 t/t4018/README\n create mode 100755 t/t4018/ada.sh\n delete mode 100644 t/t4018/bash-arithmetic-function\n delete mode 100644 t/t4018/bash-bashism-style-compact\n delete mode 100644 t/t4018/bash-bashism-style-function\n delete mode 100644 t/t4018/bash-bashism-style-whitespace\n delete mode 100644 t/t4018/bash-conditional-function\n delete mode 100644 t/t4018/bash-missing-parentheses\n delete mode 100644 t/t4018/bash-mixed-style-compact\n delete mode 100644 t/t4018/bash-mixed-style-function\n delete mode 100644 t/t4018/bash-nested-functions\n delete mode 100644 t/t4018/bash-other-characters\n delete mode 100644 t/t4018/bash-posix-style-compact\n delete mode 100644 t/t4018/bash-posix-style-function\n delete mode 100644 t/t4018/bash-posix-style-whitespace\n delete mode 100644 t/t4018/bash-subshell-function\n delete mode 100644 t/t4018/bash-trailing-comment\n create mode 100755 t/t4018/bash.sh\n delete mode 100644 t/t4018/cpp-c++-function\n delete mode 100644 t/t4018/cpp-class-constructor\n delete mode 100644 t/t4018/cpp-class-constructor-mem-init\n delete mode 100644 t/t4018/cpp-class-definition\n delete mode 100644 t/t4018/cpp-class-definition-derived\n delete mode 100644 t/t4018/cpp-class-destructor\n delete mode 100644 t/t4018/cpp-function-returning-global-type\n delete mode 100644 t/t4018/cpp-function-returning-nested\n delete mode 100644 t/t4018/cpp-function-returning-pointer\n delete mode 100644 t/t4018/cpp-function-returning-reference\n delete mode 100644 t/t4018/cpp-gnu-style-function\n delete mode 100644 t/t4018/cpp-namespace-definition\n delete mode 100644 t/t4018/cpp-operator-definition\n delete mode 100644 t/t4018/cpp-skip-access-specifiers\n delete mode 100644 t/t4018/cpp-skip-comment-block\n delete mode 100644 t/t4018/cpp-skip-labels\n delete mode 100644 t/t4018/cpp-struct-definition\n delete mode 100644 t/t4018/cpp-struct-single-line\n delete mode 100644 t/t4018/cpp-template-function-definition\n delete mode 100644 t/t4018/cpp-union-definition\n delete mode 100644 t/t4018/cpp-void-c-function\n create mode 100755 t/t4018/cpp.sh\n delete mode 100644 t/t4018/css-attribute-value-selector\n delete mode 100644 t/t4018/css-block-level-@-statements\n delete mode 100644 t/t4018/css-brace-in-col-1\n delete mode 100644 t/t4018/css-class-selector\n delete mode 100644 t/t4018/css-colon-eol\n delete mode 100644 t/t4018/css-colon-selector\n delete mode 100644 t/t4018/css-common\n delete mode 100644 t/t4018/css-id-selector\n delete mode 100644 t/t4018/css-long-selector-list\n delete mode 100644 t/t4018/css-prop-sans-indent\n delete mode 100644 t/t4018/css-root-selector\n delete mode 100644 t/t4018/css-short-selector-list\n delete mode 100644 t/t4018/css-trailing-space\n create mode 100755 t/t4018/css.sh\n create mode 100755 t/t4018/custom.sh\n delete mode 100644 t/t4018/custom1-pattern\n delete mode 100644 t/t4018/custom2-match-to-end-of-line\n delete mode 100644 t/t4018/custom3-alternation-in-pattern\n delete mode 100644 t/t4018/dts-labels\n delete mode 100644 t/t4018/dts-node-unitless\n delete mode 100644 t/t4018/dts-nodes\n delete mode 100644 t/t4018/dts-nodes-boolean-prop\n delete mode 100644 t/t4018/dts-nodes-comment1\n delete mode 100644 t/t4018/dts-nodes-comment2\n delete mode 100644 t/t4018/dts-nodes-multiline-prop\n delete mode 100644 t/t4018/dts-reference\n delete mode 100644 t/t4018/dts-root\n delete mode 100644 t/t4018/dts-root-comment\n create mode 100755 t/t4018/dts.sh\n delete mode 100644 t/t4018/elixir-do-not-pick-end\n delete mode 100644 t/t4018/elixir-ex-unit-test\n delete mode 100644 t/t4018/elixir-function\n delete mode 100644 t/t4018/elixir-macro\n delete mode 100644 t/t4018/elixir-module\n delete mode 100644 t/t4018/elixir-module-func\n delete mode 100644 t/t4018/elixir-nested-module\n delete mode 100644 t/t4018/elixir-private-function\n delete mode 100644 t/t4018/elixir-protocol\n delete mode 100644 t/t4018/elixir-protocol-implementation\n create mode 100755 t/t4018/elixir.sh\n delete mode 100644 t/t4018/fortran-block-data\n delete mode 100644 t/t4018/fortran-comment\n delete mode 100644 t/t4018/fortran-comment-keyword\n delete mode 100644 t/t4018/fortran-comment-legacy\n delete mode 100644 t/t4018/fortran-comment-legacy-star\n delete mode 100644 t/t4018/fortran-external-function\n delete mode 100644 t/t4018/fortran-external-subroutine\n delete mode 100644 t/t4018/fortran-module\n delete mode 100644 t/t4018/fortran-module-procedure\n delete mode 100644 t/t4018/fortran-program\n create mode 100755 t/t4018/fortran.sh\n delete mode 100644 t/t4018/fountain-scene\n create mode 100755 t/t4018/fountain.sh\n delete mode 100644 t/t4018/golang-complex-function\n delete mode 100644 t/t4018/golang-func\n delete mode 100644 t/t4018/golang-interface\n delete mode 100644 t/t4018/golang-long-func\n delete mode 100644 t/t4018/golang-struct\n create mode 100755 t/t4018/golang.sh\n delete mode 100644 t/t4018/java-class-member-function\n create mode 100755 t/t4018/java.sh\n delete mode 100644 t/t4018/markdown-heading-indented\n delete mode 100644 t/t4018/markdown-heading-non-headings\n create mode 100755 t/t4018/markdown.sh\n delete mode 100644 t/t4018/matlab-class-definition\n delete mode 100644 t/t4018/matlab-function\n delete mode 100644 t/t4018/matlab-octave-section-1\n delete mode 100644 t/t4018/matlab-octave-section-2\n delete mode 100644 t/t4018/matlab-section\n create mode 100755 t/t4018/matlab.sh\n delete mode 100644 t/t4018/perl-skip-end-of-heredoc\n delete mode 100644 t/t4018/perl-skip-forward-decl\n delete mode 100644 t/t4018/perl-skip-sub-in-pod\n delete mode 100644 t/t4018/perl-sub-definition\n delete mode 100644 t/t4018/perl-sub-definition-kr-brace\n create mode 100755 t/t4018/perl.sh\n delete mode 100644 t/t4018/php-abstract-class\n delete mode 100644 t/t4018/php-abstract-method\n delete mode 100644 t/t4018/php-class\n delete mode 100644 t/t4018/php-final-class\n delete mode 100644 t/t4018/php-final-method\n delete mode 100644 t/t4018/php-function\n delete mode 100644 t/t4018/php-interface\n delete mode 100644 t/t4018/php-method\n delete mode 100644 t/t4018/php-trait\n create mode 100755 t/t4018/php.sh\n delete mode 100644 t/t4018/python-async-def\n delete mode 100644 t/t4018/python-class\n delete mode 100644 t/t4018/python-def\n delete mode 100644 t/t4018/python-indented-async-def\n delete mode 100644 t/t4018/python-indented-class\n delete mode 100644 t/t4018/python-indented-def\n create mode 100755 t/t4018/python.sh\n create mode 100755 t/t4018/ruby.sh\n delete mode 100644 t/t4018/rust-fn\n delete mode 100644 t/t4018/rust-impl\n delete mode 100644 t/t4018/rust-macro-rules\n delete mode 100644 t/t4018/rust-struct\n delete mode 100644 t/t4018/rust-trait\n create mode 100755 t/t4018/rust.sh\n\nRange-diff:\n -:  ---------- >  1:  305fc646d0 userdiff: refactor away the parse_bool() function\n 1:  7dbe7d638e !  2:  989438c46a userdiff: re-order builtin drivers in alphabetical order\n    @@ Metadata\n     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## Commit message ##\n    -    userdiff: re-order builtin drivers in alphabetical order\n    +    userdiff style: re-order drivers in alphabetical order\n     \n         Address some old code smell and move around the built-in userdiff\n    -    definitions so they're both in alphabetical order, and now in the same\n    +    drivers so they're both in alphabetical order, and now in the same\n         order they appear in the gitattributes(5) documentation.\n     \n         The two started drifting in be58e70dba (diff: unify external diff and\n -:  ---------- >  3:  4c48e5532c userdiff style: declare patterns with consistent style\n -:  ---------- >  4:  f41fa5b316 userdiff style: normalize pascal regex declaration\n 2:  69532bfb68 =  5:  0875d5205c userdiff: add and use for_each_userdiff_driver()\n 3:  6ad7a5f608 =  6:  638247d04d userdiff tests: explicitly test \"default\" pattern\n 4:  48b8c39380 !  7:  219043a488 userdiff tests: list builtin drivers via test-tool\n    @@ t/helper/test-userdiff.c (new)\n     +#include \"cache.h\"\n     +#include \"userdiff.h\"\n     +\n    -+static int userdiff_list_builtin_drivers_cb(struct userdiff_driver *driver,\n    -+\t\t\t\t\t    enum userdiff_driver_type type,\n    -+\t\t\t\t\t    void *priv)\n    ++static int driver_cb(struct userdiff_driver *driver,\n    ++\t\t     enum userdiff_driver_type type, void *priv)\n     +{\n     +\tputs(driver->name);\n     +\treturn 0;\n    @@ t/helper/test-userdiff.c (new)\n     +\n     +static int list_what(enum userdiff_driver_type type)\n     +{\n    -+\treturn for_each_userdiff_driver(userdiff_list_builtin_drivers_cb, type,\n    -+\t\t\t\t\tNULL);\n    ++\treturn for_each_userdiff_driver(driver_cb, type, NULL);\n     +}\n     +\n     +int cmd__userdiff(int argc, const char **argv)\n    @@ t/t4018-diff-funcname.sh: test_description='Test custom diff function name patte\n      . ./test-lib.sh\n      \n      test_expect_success 'setup' '\n    -+\ttest-tool userdiff list-builtin-drivers >builtin-drivers &&\n    -+\ttest_file_not_empty builtin-drivers &&\n    -+\tbuiltin_drivers=$(cat builtin-drivers) &&\n    ++\tbuiltin_drivers=$(test-tool userdiff list-builtin-drivers) &&\n    ++\ttest -n \"$builtin_drivers\" &&\n     +\n      \t# a non-trivial custom pattern\n      \tgit config diff.custom1.funcname \"!static\n 5:  3073d07409 =  8:  eb66160aac userdiff: remove support for \"broken\" tests\n 6:  fd93d18351 !  9:  c6c54039e2 userdiff tests: match full hunk headers\n    @@ Commit message\n         towards even better test coverage. I'm structuring these tests in such\n         a way as to benefit from the diff.colorMove detection.\n     \n    +    The \"sed -n -e\" here was originally a single 's/^.*@@\\( \\|$\\)//p'\n    +    pattern, but the '\\( \\|$\\)' part had portability issues on OSX and\n    +    AIX.\n    +\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## t/t4018-diff-funcname.sh ##\n    @@ t/t4018-diff-funcname.sh: test_expect_success 'setup hunk header tests' '\n     -\t\tgit diff -U1 $i >actual &&\n     -\t\tgrep '@@ .* @@.*RIGHT' actual\n     +\t\tgit diff -U1 $i >diff &&\n    -+\t\tsed -n -e 's/^.*@@\\( \\|$\\)//p' <diff >ctx &&\n    ++\t\tsed -n -e 's/^.*@@$//p' -e 's/^.*@@ //p' <diff >ctx &&\n     +\t\ttest_cmp $i.ctx ctx\n      \t\"\n      done\n    @@ t/t4018/README\n     -of equal signs.\n     +The text that must appear in the hunk header must contains the word\n     +\"RIGHT\" by convention. The \"LANG-whatever.ctx\" file contains what we\n    -+expect to appear in the hunk header. We munged the start of the line\n    -+to \"@@ [...] @@\" for ease of not having to hardcode the line numbers\n    -+and offsets.\n    ++expect to appear in the hunk header. We munged away the starting \"@@\n    ++[...] @@\" part of the line for ease of not having to hardcode the line\n    ++numbers and offsets.\n     \n      ## t/t4018/README.ctx (new) ##\n     @@\n 8:  034ab9f85b ! 10:  1c6ddf96f6 blame tests: don't rely on t/t4018/ directory\n    @@ Commit message\n         with userdiff driver, 2020-11-01) so that the blame tests don't rely\n         on stealing the contents of \"t/t4018/fortran-external-function\".\n     \n    -    I'm about to refactor that directory, just moving the relevant test\n    -    file here inline is the easiest solution, and I think also the most\n    -    readable.\n    +    I'm about to refactor that directory to delete that file, just moving\n    +    the relevant test file here inline is the easiest solution, and I\n    +    think also the most readable.\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n -:  ---------- > 11:  8a883d8799 blame tests: simplify userdiff driver test\n 7:  4f8df97207 ! 12:  e56a7a6b5f userdiff tests: rewrite hunk header test infrastructure\n    @@ t/t4018-diff-funcname.sh: test_expect_success 'last regexp must not be negated'\n     +\n     +\ttest_expect_success \"$desc\" '\n     +\t\tgit diff -U1 \"$what\" >diff &&\n    -+\t\tsed -n -e \"s/^.*@@\\( \\|$\\)//p\" <diff >actual &&\n    ++\t\tsed -n -e \"s/^.*@@$//p\" -e \"s/^.*@@ //p\" <diff >actual &&\n     +\t\ttest_cmp expected actual\n     +\t'\n     +}\n    @@ t/t4018-diff-funcname.sh: test_expect_success 'last regexp must not be negated'\n      do\n     -\ttest_expect_success \"hunk header: $i\" \"\n     -\t\tgit diff -U1 $i >diff &&\n    --\t\tsed -n -e 's/^.*@@\\( \\|$\\)//p' <diff >ctx &&\n    +-\t\tsed -n -e 's/^.*@@$//p' -e 's/^.*@@ //p' <diff >ctx &&\n     -\t\ttest_cmp $i.ctx ctx\n     -\t\"\n     +\ttest=\"$TEST_DIRECTORY/t4018/$what.sh\"\n    @@ t/t4018/README (deleted)\n     -\n     -The text that must appear in the hunk header must contains the word\n     -\"RIGHT\" by convention. The \"LANG-whatever.ctx\" file contains what we\n    --expect to appear in the hunk header. We munged the start of the line\n    --to \"@@ [...] @@\" for ease of not having to hardcode the line numbers\n    --and offsets.\n    +-expect to appear in the hunk header. We munged away the starting \"@@\n    +-[...] @@\" part of the line for ease of not having to hardcode the line\n    +-numbers and offsets.\n     \n      ## t/t4018/README.ctx (deleted) ##\n     @@\n -:  ---------- > 13:  84d20a7cd0 userdiff tests: do config teardown in test_diff_funcname()\n 9:  e48ad2b57f ! 14:  70fc9fa565 userdiff tests: move custom patterns into one test file\n    @@ Commit message\n     \n      ## t/t4018-diff-funcname.sh ##\n     @@ t/t4018-diff-funcname.sh: test_expect_success 'setup' '\n    - \ttest_file_not_empty builtin-drivers &&\n    - \tbuiltin_drivers=$(cat builtin-drivers) &&\n    + \tbuiltin_drivers=$(test-tool userdiff list-builtin-drivers) &&\n    + \ttest -n \"$builtin_drivers\" &&\n      \n     -\t# a non-trivial custom pattern\n     -\tgit config diff.custom1.funcname \"!static\n    @@ t/t4018/custom.sh (new)\n     +\t}\n     +}\n     +EOF_TEST\n    -+\n    -+test_expect_success 'custom: teardown' '\n    -+\ttest_unconfig diff.custom.funcname &&\n    -+\ttest_unconfig diff.custom.xfuncname\n    -+'\n     \n      ## t/t4018/custom1.sh (deleted) ##\n     @@\n10:  6a4224b512 = 15:  8539d6d464 userdiff tests: remove hack for \"RIGHT\" token\n -:  ---------- > 16:  121e5d6dfa userdiff tests: do not do compile tests on \"custom\" pattern\n12:  9ce7de5698 ! 17:  451b7ae453 userdiff tests + docs: document & test \"diff.<driver>.x?funcname\"\n    @@ Documentation/config/diff.txt: diff.<driver>.command::\n      \tSee linkgit:gitattributes[5] for details.\n     ++\n     +When provided as `diff.<driver>.funcname` the regular expression is\n    -+interpreted as a basic regular expression, with\n    ++interpreted as a basic regular expression. With\n     +`diff.<driver>.xfuncname` it's interpreted as an extended regular\n     +expression.\n     ++\n    -+\n     +The `*.funcname` and `*.xfuncname` variables behave as if though they\n     +were one configuration variable for the purposes of what value\n     +eventually gets used. Setting `*.funcname` will override an earlier\n    @@ Documentation/config/diff.txt: diff.<driver>.command::\n     \n      ## t/t4018/custom.sh ##\n     @@ t/t4018/custom.sh: public class Beer\n    + \t}\n      }\n      EOF_TEST\n    - \n    -+test_expect_success 'custom; setup config precedence' '\n    ++\n    ++test_expect_success 'custom: setup config precedence' '\n     +\tgit config diff.custom.funcname \"foo\" &&\n     +\tgit config diff.custom.xfuncname \"bar\"\n     +'\n    @@ t/t4018/custom.sh: public class Beer\n     +baz\n     +EOF_TEST\n     +\n    -+test_expect_success 'custom: teardown' '\n    -+\ttest_unconfig diff.custom.funcname &&\n    -+\ttest_unconfig diff.custom.xfuncname\n    -+'\n    -+\n    -+test_expect_success 'custom; setup config precedence' '\n    ++test_expect_success 'custom: setup config precedence' '\n     +\tgit config diff.custom.xfuncname \"bar\" &&\n     +\tgit config diff.custom.funcname \"foo\"\n    -+\n     +'\n     +\n     +test_diff_funcname 'custom: config precedence' \\\n    @@ t/t4018/custom.sh: public class Beer\n     +\n     +baz\n     +EOF_TEST\n    -+\n    - test_expect_success 'custom: teardown' '\n    - \ttest_unconfig diff.custom.funcname &&\n    - \ttest_unconfig diff.custom.xfuncname\n13:  3a9f64b747 ! 18:  5a402bb9bf gitattributes doc: reword discussion of built-in userdiff patterns\n    @@ Commit message\n         \"diff.tex.xfuncname\" example which now immediately precedes it. This\n         will make a follow-up commit smaller.\n     \n    +    Helped-by: Eric Sunshine <sunshine@sunshineco.com>\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## Documentation/gitattributes.txt ##\n    @@ Documentation/gitattributes.txt: backslashes; the pattern above picks a line tha\n     +There are built-in patterns shipped as part of git itself. A more\n     +advanced version of the `tex` pattern discussed above is one of them.\n     +\n    -+For built-in patterns you do not need the \"diff.tex.xfuncname\"\n    -+discussed above in your configuration file, but if present it'll\n    -+override the built-in pattern.\n    ++For built-in patterns, you do not need `diff.<lang>.xfuncname` in your\n    ++configuration file as discussed above, but if present, it will\n    ++override a built-in pattern.\n     +\n    -+You still need to enable built-in patterns with the the attribute\n    -+mechanism, via `.gitattributes`).\n    ++Nevertheless, you need to enable built-in patterns via .gitattributes`\n    ++for the pattern to take effect.\n     +\n    -+The following built in patterns are available:\n    ++The following built-in patterns are available:\n      \n      - `ada` suitable for source code in the Ada language.\n      \n14:  a3e8d0bfc1 ! 19:  a3badb1a3e gitattributes doc: document multi-line userdiff patterns\n    @@ Documentation/gitattributes.txt: backslashes; the pattern above picks a line tha\n      backslash, and zero or more occurrences of `sub` followed by\n      `section` followed by open brace, to the end of line.\n      \n    -+Multiple patterns can be supplied by seperating them with\n    -+newlines. They will be matched one at a time and are compiled as\n    -+separate patterns, and thus the first capture in each such pattern is\n    -+`$1`, see further discussion of captures below.\n    +-There are built-in patterns shipped as part of git itself. A more\n    +-advanced version of the `tex` pattern discussed above is one of them.\n    ++Multiple patterns can be supplied by listing them one per line\n    ++separated by `\\n`. They will be matched one at a time from left to\n    ++right. Do not supply a trailing \"\\n\" for the last pattern. E.g.:\n     +\n    -+Patterns that begin with \"!\" are negated (to match a literal \"!\" at\n    -+the start of a line use e.g. \"[!]\"). A matching negated pattern will\n    -+cause the matching line to be skipped. Use it to blacklist otherwise\n    -+matching non-negated patterns. The last pattern must not be negated,\n    -+we'll error out if that's the case.\n    ++------------------------\n    ++[diff \"perl\"]\n    ++\txfuncname = \"!^=head\\n^[^ ]+.*\"\n    ++------------------------\n    ++\n    ++Patterns in in a list of multiple that begin with \"!\" are negated. A\n    ++matching negated pattern will cause the matched line to be\n    ++skipped. Use it to skip a later pattern that would otherwise match. It\n    ++is an error if one or more negated patterns aren't followed by a\n    ++non-negated pattern.\n    ++\n    ++To match a literal \"!\" at the start of a line, use some other regex\n    ++construct that will match a literal \"!\" without \"!\" being the first\n    ++character on that line, such as \"[!]\".\n     +\n     +If the pattern contains a `$1` capture it will be used instead of the\n     +entire matching line (`$0`) to display the hunk header. This can be\n    @@ Documentation/gitattributes.txt: backslashes; the pattern above picks a line tha\n     +only display the function name as part of a longer function\n     +definition.\n     +\n    - There are built-in patterns shipped as part of git itself. A more\n    - advanced version of the `tex` pattern discussed above is one of them.\n    ++There are built-in patterns shipped as part of git itself, see the\n    ++full listing below.\n    + \n    + For built-in patterns, you do not need `diff.<lang>.xfuncname` in your\n    +-configuration file as discussed above, but if present, it will\n    +-override a built-in pattern.\n    ++configuration file. If present, it will override a built-in pattern,\n    ++as shown in the `diff.perl.xfuncname` example above.\n      \n    + Nevertheless, you need to enable built-in patterns via .gitattributes`\n    + for the pattern to take effect.\n     \n      ## t/t4018/custom.sh ##\n    -@@ t/t4018/custom.sh: test_expect_success 'custom: teardown' '\n    - \ttest_unconfig diff.custom.funcname &&\n    - \ttest_unconfig diff.custom.xfuncname\n    - '\n    +@@ t/t4018/custom.sh: ChangeMe\n    + \n    + baz\n    + EOF_TEST\n     +\n    -+test_expect_success 'custom: negation syntax, ! is magic' '\n    ++test_expect_success 'custom: setup negation syntax, ! is magic' '\n     +\tgit config diff.custom.xfuncname \"!negation\n     +line\"\n     +'\n     +\n    -+test_diff_funcname 'custom: config precedence' \\\n    ++test_diff_funcname 'custom: negation syntax, ! is magic' \\\n     +\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n     +line\n     +EOF_HUNK\n    @@ t/t4018/custom.sh: test_expect_success 'custom: teardown' '\n     +baz\n     +EOF_TEST\n     +\n    -+test_expect_success 'custom: negation syntax, use [!] to override ! magic' '\n    ++test_expect_success 'custom: setup negation syntax, use [!] to override ! magic' '\n     +\tgit config diff.custom.xfuncname \"[!]negation\n     +line\"\n     +'\n     +\n    -+test_diff_funcname 'custom: config precedence' \\\n    ++test_diff_funcname 'custom: negation syntax, use [!] to override ! magic' \\\n     +\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n     +!negation\n     +EOF_HUNK\n    @@ t/t4018/custom.sh: test_expect_success 'custom: teardown' '\n     +baz\n     +EOF_TEST\n     +\n    ++test_expect_success 'custom: setup captures in multiple patterns' '\n    ++\tgit config diff.custom.xfuncname \"!^=head\n    ++^format ([^ ]+)\n    ++^sub ([^;]+)\"\n    ++'\n    ++\n    ++test_diff_funcname 'custom: captures in multiple patterns' \\\n    ++\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n    ++foo\n    ++EOF_HUNK\n    ++sub foo;\n    ++=head1\n    ++ChangeMe\n    ++\n    ++EOF_TEST\n    +\n    + ## t/t4018/perl.sh ##\n    +@@ t/t4018/perl.sh: sub RIGHT\n    + \tprint \"ChangeMe\\n\";\n    + }\n    + EOF_TEST\n    ++\n    ++\n    ++test_expect_success 'custom: setup config overrides built-in patterns' '\n    ++\tgit config diff.perl.xfuncname \"!^=head\n    ++^[^ ]+.*\"\n    ++'\n    ++\n    ++test_diff_funcname 'custom: config overrides built-in patterns' \\\n    ++\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n    ++sub foo;\n    ++EOF_HUNK\n    ++sub foo;\n    ++=head1\n    ++ChangeMe\n    ++\n    ++EOF_TEST\n15:  b83cf4da91 ! 20:  1b46726e85 userdiff tests: remove \"funcname\" from custom3 test\n    @@ t/t4018/custom.sh: public class Beer\n      \tgit config diff.custom.xfuncname \"^[ \t]*((public|static).*)$\"\n      '\n      \n    -@@ t/t4018/custom.sh: test_diff_funcname 'custom: config precedence' \\\n    - line\n    - EOF_HUNK\n    - line\n    -+\n    - !negation\n    - \n    - ChangeMe\n16:  f286e42540 ! 21:  9a18506aff userdiff tests: factor out test_diff_funcname() logic\n    @@ t/t4018-diff-funcname.sh: test_expect_success 'last regexp must not be negated'\n     +\n     +last_diff_context_line () {\n     +\tfile=$1\n    -+\tsed -n -e \"s/^.*@@\\( \\|$\\)//p\" <$file\n    ++\tsed -n -e \"s/^.*@@$//p\" -e \"s/^.*@@ //p\" <$file\n     +}\n     +\n      test_diff_funcname () {\n    @@ t/t4018-diff-funcname.sh: test_diff_funcname () {\n      \n      \ttest_expect_success \"$desc\" '\n      \t\tgit diff -U1 \"$what\" >diff &&\n    --\t\tsed -n -e \"s/^.*@@\\( \\|$\\)//p\" <diff >actual &&\n    +-\t\tsed -n -e \"s/^.*@@$//p\" -e \"s/^.*@@ //p\" <diff >actual &&\n     +\t\tlast_diff_context_line diff >actual &&\n      \t\ttest_cmp expected actual\n    - \t'\n    - }\n    + \t' &&\n    + \n17:  12635fefd6 ! 22:  24548fb680 userdiff tests: test hunk headers on accumulated files\n    @@ t/t4018-diff-funcname.sh: test_diff_funcname () {\n      \t\tgit diff -U1 \"$what\" >diff &&\n      \t\tlast_diff_context_line diff >actual &&\n      \t\ttest_cmp expected actual\n    -+\t' &&\n    -+\n    + \t' &&\n    + \n     +\ttest_expect_success \"$desc (accumulated)\" '\n     +\t\tgit diff -U1 \"$what\".acc >diff &&\n     +\t\tlast_diff_context_line diff >actual.lines &&\n     +\t\ttail -n 1 actual.lines >actual &&\n     +\t\ttest_cmp expected actual\n    - \t'\n    - }\n    - \n    ++\t' &&\n    ++\n    + \ttest_expect_success \"teardown: $desc\" '\n    + \t\t# In case any custom config was set immediately before\n    + \t\t# the test itself in the test file\n     @@ t/t4018-diff-funcname.sh: do\n      \t\techo \"$what\" >arg.what\n      \t' &&\n18:  3eef2896ea = 23:  48f00a59d5 userdiff tests: test hunk header selection with -U0\n19:  3061fac48c ! 24:  05a01990c9 userdiff tests: assert empty hunk header context on -U<large>\n    @@ Commit message\n     \n      ## t/t4018-diff-funcname.sh ##\n     @@ t/t4018-diff-funcname.sh: test_diff_funcname () {\n    - \t\ttail -n 1 actual.lines >actual &&\n      \t\ttest_cmp expected actual\n    - \t'\n    -+\n    + \t' &&\n    + \n     +\ttest_expect_success \"$desc -U9001 (accumulated)\" '\n     +\t\tgit diff -U9001 \"$what\".acc >diff &&\n     +\t\tlast_diff_context_line diff >actual.lines &&\n     +\t\ttail -n 1 actual.lines >actual &&\n     +\t\techo >blank &&\n     +\t\ttest_cmp blank actual\n    -+\t'\n    - }\n    - \n    - for what in $diffpatterns\n    ++\t' &&\n    ++\n    + \ttest_expect_success \"teardown: $desc\" '\n    + \t\t# In case any custom config was set immediately before\n    + \t\t# the test itself in the test file\n11:  0901623546 ! 25:  3d2f42d704 userdiff: match \"package\" in diff=golang\n    @@ t/t4018/golang.sh\n      func (t *Test) RIGHT(a Type) (Type, error) {\n     \n      ## userdiff.c ##\n    -@@ userdiff.c: IPATTERN(\"fortran\",\n    - IPATTERN(\"fountain\", \"^((\\\\.[^.]|(int|ext|est|int\\\\.?/ext|i/e)[. ]).*)$\",\n    +@@ userdiff.c: IPATTERN(\"fountain\",\n    + \t /* -- */\n      \t \"[^ \\t-]+\"),\n      PATTERNS(\"golang\",\n     +\t /* Packages */\n -:  ---------- > 26:  b2e16ade06 userdiff tests: add basic test for ada\n -:  ---------- > 27:  826b6f4d6a userdiff tests: add basic test for ruby\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416918","messageId":"20210215154427.32693-12-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215005236.11313-1-avarab@gmail.com","subject":"[PATCH v2 11/27] blame tests: simplify userdiff driver test","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T15:44:11Z","receivedAt":"2021-02-15T15:48:14Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Simplify the test added in 9466e3809d (blame: enable funcname blaming\nwith userdiff driver, 2020-11-01) to use the --author support recently\nadded in 999cfc4f45 (test-lib functions: add --author support to\ntest_commit, 2021-01-12).\n\nWe also did not need the full fortran-external-function content, let's\ncut it down to just the important parts, and further modify it to\ndemonstrate that the fortran-specific userdiff function is in effect\nby adding \"WRONG\" lines surrounding the \"RIGHT\" one.\n\nThe test also left behind a .gitattributes files, let's clean it up\nwith \"test_when_finished\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/annotate-tests.sh | 36 +++++++++++++++---------------------\n 1 file changed, 15 insertions(+), 21 deletions(-)\n\ndiff --git a/t/annotate-tests.sh b/t/annotate-tests.sh\nindex 04a2c58594..4a86e0f349 100644\n--- a/t/annotate-tests.sh\n+++ b/t/annotate-tests.sh\n@@ -479,32 +479,26 @@ test_expect_success 'blame -L ^:RE (absolute: end-of-file)' '\n \tcheck_count -f hello.c -L$n -L^:ma.. F 4 G 1 H 1\n '\n \n-test_expect_success 'setup -L :funcname with userdiff driver' '\n-\techo \"fortran-* diff=fortran\" >.gitattributes &&\n-\tfortran_file=fortran-external-function &&\n-\tcat >$fortran_file <<-\\EOF &&\n+test_expect_success 'blame -L :funcname with userdiff driver' '\n+\tcat >file.template <<-\\EOF &&\n+\tdef WRONG begin end\n \tfunction RIGHT(a, b) result(c)\n+\tint WRONG(void) {}\n \n \tinteger, intent(in) :: ChangeMe\n-\tinteger, intent(in) :: b\n-\tinteger, intent(out) :: c\n-\n-\tc = a+b\n-\n-\tend function RIGHT\n \tEOF\n-\tgit add \"$fortran_file\" &&\n-\tGIT_AUTHOR_NAME=\"A\" GIT_AUTHOR_EMAIL=\"A@test.git\" \\\n-\tgit commit -m \"add fortran file\" &&\n-\tsed -e \"s/ChangeMe/IWasChanged/\" <\"$fortran_file\" >\"$fortran_file\".tmp &&\n-\tmv \"$fortran_file\".tmp \"$fortran_file\" &&\n-\tgit add \"$fortran_file\" &&\n-\tGIT_AUTHOR_NAME=\"B\" GIT_AUTHOR_EMAIL=\"B@test.git\" \\\n-\tgit commit -m \"change fortran file\"\n-'\n \n-test_expect_success 'blame -L :funcname with userdiff driver' '\n-\tcheck_count -f fortran-external-function -L:RIGHT A 7 B 1\n+\tfortran_file=file.f03 &&\n+\ttest_when_finished \"rm .gitattributes\" &&\n+\techo \"$fortran_file diff=fortran\" >.gitattributes &&\n+\n+\ttest_commit --author \"A <A@test.git>\" \\\n+\t\t\"add\" $fortran_file \\\n+\t\t\"$(cat file.template)\" &&\n+\ttest_commit --author \"B <B@test.git>\" \\\n+\t\t\"change\" $fortran_file \\\n+\t\t\"$(cat file.template | sed -e s/ChangeMe/IWasChanged/)\" &&\n+\tcheck_count -f $fortran_file -L:RIGHT A 3 B 1\n '\n \n test_expect_success 'setup incremental' '\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416919","messageId":"20210215154427.32693-11-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215005236.11313-1-avarab@gmail.com","subject":"[PATCH v2 10/27] blame tests: don't rely on t/t4018/ directory","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T15:44:10Z","receivedAt":"2021-02-15T15:48:37Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Refactor a test added in 9466e3809d (blame: enable funcname blaming\nwith userdiff driver, 2020-11-01) so that the blame tests don't rely\non stealing the contents of \"t/t4018/fortran-external-function\".\n\nI'm about to refactor that directory to delete that file, just moving\nthe relevant test file here inline is the easiest solution, and I\nthink also the most readable.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/annotate-tests.sh | 16 +++++++++++++---\n 1 file changed, 13 insertions(+), 3 deletions(-)\n\ndiff --git a/t/annotate-tests.sh b/t/annotate-tests.sh\nindex 29ce89090d..04a2c58594 100644\n--- a/t/annotate-tests.sh\n+++ b/t/annotate-tests.sh\n@@ -482,12 +482,22 @@ test_expect_success 'blame -L ^:RE (absolute: end-of-file)' '\n test_expect_success 'setup -L :funcname with userdiff driver' '\n \techo \"fortran-* diff=fortran\" >.gitattributes &&\n \tfortran_file=fortran-external-function &&\n-\torig_file=\"$TEST_DIRECTORY/t4018/$fortran_file\" &&\n-\tcp \"$orig_file\" . &&\n+\tcat >$fortran_file <<-\\EOF &&\n+\tfunction RIGHT(a, b) result(c)\n+\n+\tinteger, intent(in) :: ChangeMe\n+\tinteger, intent(in) :: b\n+\tinteger, intent(out) :: c\n+\n+\tc = a+b\n+\n+\tend function RIGHT\n+\tEOF\n \tgit add \"$fortran_file\" &&\n \tGIT_AUTHOR_NAME=\"A\" GIT_AUTHOR_EMAIL=\"A@test.git\" \\\n \tgit commit -m \"add fortran file\" &&\n-\tsed -e \"s/ChangeMe/IWasChanged/\" <\"$orig_file\" >\"$fortran_file\" &&\n+\tsed -e \"s/ChangeMe/IWasChanged/\" <\"$fortran_file\" >\"$fortran_file\".tmp &&\n+\tmv \"$fortran_file\".tmp \"$fortran_file\" &&\n \tgit add \"$fortran_file\" &&\n \tGIT_AUTHOR_NAME=\"B\" GIT_AUTHOR_EMAIL=\"B@test.git\" \\\n \tgit commit -m \"change fortran file\"\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416920","messageId":"20210215154427.32693-8-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215005236.11313-1-avarab@gmail.com","subject":"[PATCH v2 07/27] userdiff tests: list builtin drivers via test-tool","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T15:44:07Z","receivedAt":"2021-02-15T15:49:09Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the userdiff test to list the builtin drivers via the\ntest-tool, using the new for_each_userdiff_driver() API function.\n\nThis gets rid of the need to modify this part of the test every time a\nnew pattern is added, see 2ff6c34612 (userdiff: support Bash,\n2020-10-22) and 09dad9256a (userdiff: support Markdown, 2020-05-02)\nfor two recent examples.\n\nI only need the \"list-builtin-drivers \"argument here, but let's add\n\"list-custom-drivers\" and \"list-drivers\" too, just because it's easy.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Makefile                 |  1 +\n t/helper/test-tool.c     |  1 +\n t/helper/test-tool.h     |  1 +\n t/helper/test-userdiff.c | 30 ++++++++++++++++++++++++++++++\n t/t4018-diff-funcname.sh | 28 ++++------------------------\n 5 files changed, 37 insertions(+), 24 deletions(-)\n create mode 100644 t/helper/test-userdiff.c\n\ndiff --git a/Makefile b/Makefile\nindex 5a239cac20..710a0deaed 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -741,6 +741,7 @@ TEST_BUILTINS_OBJS += test-submodule-nested-repo-config.o\n TEST_BUILTINS_OBJS += test-subprocess.o\n TEST_BUILTINS_OBJS += test-trace2.o\n TEST_BUILTINS_OBJS += test-urlmatch-normalization.o\n+TEST_BUILTINS_OBJS += test-userdiff.o\n TEST_BUILTINS_OBJS += test-wildmatch.o\n TEST_BUILTINS_OBJS += test-windows-named-pipe.o\n TEST_BUILTINS_OBJS += test-write-cache.o\ndiff --git a/t/helper/test-tool.c b/t/helper/test-tool.c\nindex f97cd9f48a..dcb05ca6e5 100644\n--- a/t/helper/test-tool.c\n+++ b/t/helper/test-tool.c\n@@ -71,6 +71,7 @@ static struct test_cmd cmds[] = {\n \t{ \"submodule-nested-repo-config\", cmd__submodule_nested_repo_config },\n \t{ \"subprocess\", cmd__subprocess },\n \t{ \"trace2\", cmd__trace2 },\n+\t{ \"userdiff\", cmd__userdiff },\n \t{ \"urlmatch-normalization\", cmd__urlmatch_normalization },\n \t{ \"xml-encode\", cmd__xml_encode },\n \t{ \"wildmatch\", cmd__wildmatch },\ndiff --git a/t/helper/test-tool.h b/t/helper/test-tool.h\nindex 28072c0ad5..589f2e8ac6 100644\n--- a/t/helper/test-tool.h\n+++ b/t/helper/test-tool.h\n@@ -61,6 +61,7 @@ int cmd__submodule_config(int argc, const char **argv);\n int cmd__submodule_nested_repo_config(int argc, const char **argv);\n int cmd__subprocess(int argc, const char **argv);\n int cmd__trace2(int argc, const char **argv);\n+int cmd__userdiff(int argc, const char **argv);\n int cmd__urlmatch_normalization(int argc, const char **argv);\n int cmd__xml_encode(int argc, const char **argv);\n int cmd__wildmatch(int argc, const char **argv);\ndiff --git a/t/helper/test-userdiff.c b/t/helper/test-userdiff.c\nnew file mode 100644\nindex 0000000000..1e17aeb265\n--- /dev/null\n+++ b/t/helper/test-userdiff.c\n@@ -0,0 +1,30 @@\n+#include \"test-tool.h\"\n+#include \"cache.h\"\n+#include \"userdiff.h\"\n+\n+static int driver_cb(struct userdiff_driver *driver,\n+\t\t     enum userdiff_driver_type type, void *priv)\n+{\n+\tputs(driver->name);\n+\treturn 0;\n+}\n+\n+static int list_what(enum userdiff_driver_type type)\n+{\n+\treturn for_each_userdiff_driver(driver_cb, type, NULL);\n+}\n+\n+int cmd__userdiff(int argc, const char **argv)\n+{\n+\tif (argc != 2)\n+\t\treturn 1;\n+\n+\tif (!strcmp(argv[1], \"list-drivers\"))\n+\t\treturn list_what(USERDIFF_DRIVER_TYPE_UNSPECIFIED);\n+\telse if (!strcmp(argv[1], \"list-builtin-drivers\"))\n+\t\treturn list_what(USERDIFF_DRIVER_TYPE_BUILTIN);\n+\telse if (!strcmp(argv[1], \"list-custom-drivers\"))\n+\t\treturn list_what(USERDIFF_DRIVER_TYPE_CUSTOM);\n+\telse\n+\t\treturn error(\"unknown argument %s\", argv[1]);\n+}\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex cefe329aea..6faa719536 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -8,6 +8,9 @@ test_description='Test custom diff function name patterns'\n . ./test-lib.sh\n \n test_expect_success 'setup' '\n+\tbuiltin_drivers=$(test-tool userdiff list-builtin-drivers) &&\n+\ttest -n \"$builtin_drivers\" &&\n+\n \t# a non-trivial custom pattern\n \tgit config diff.custom1.funcname \"!static\n !String\n@@ -26,30 +29,7 @@ test_expect_success 'setup' '\n '\n \n diffpatterns=\"\n-\tada\n-\tbash\n-\tbibtex\n-\tcpp\n-\tcsharp\n-\tcss\n-\tdts\n-\telixir\n-\tfortran\n-\tfountain\n-\tgolang\n-\thtml\n-\tjava\n-\tmarkdown\n-\tmatlab\n-\tobjc\n-\tpascal\n-\tperl\n-\tphp\n-\tpython\n-\truby\n-\trust\n-\ttex\n-\tdefault\n+\t$builtin_drivers\n \tcustom1\n \tcustom2\n \tcustom3\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416921","messageId":"20210215154427.32693-5-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215005236.11313-1-avarab@gmail.com","subject":"[PATCH v2 04/27] userdiff style: normalize pascal regex declaration","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T15:44:04Z","receivedAt":"2021-02-15T15:49:10Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Declare the pascal pattern consistently with how we declare the\nothers, not having \"\\n\" on one line by itself, but as part of the\npattern, and when there are alterations have the \"|\" at the start, not\nend of the line.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n userdiff.c | 5 ++---\n 1 file changed, 2 insertions(+), 3 deletions(-)\n\ndiff --git a/userdiff.c b/userdiff.c\nindex c7aaf7094f..10a02d3620 100644\n--- a/userdiff.c\n+++ b/userdiff.c\n@@ -175,9 +175,8 @@ PATTERNS(\"objc\",\n \t \"|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?\"\n \t \"|[-+*/<>%&^|=!]=|--|\\\\+\\\\+|<<=?|>>=?|&&|\\\\|\\\\||::|->\"),\n PATTERNS(\"pascal\",\n-\t \"^(((class[ \\t]+)?(procedure|function)|constructor|destructor|interface|\"\n-\t\t\"implementation|initialization|finalization)[ \\t]*.*)$\"\n-\t \"\\n\"\n+\t \"^(((class[ \\t]+)?(procedure|function)|constructor|destructor|interface\"\n+\t \"|implementation|initialization|finalization)[ \\t]*.*)$\\n\"\n \t \"^(.*=[ \\t]*(class|record).*)$\",\n \t /* -- */\n \t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416922","messageId":"20210215154427.32693-6-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215005236.11313-1-avarab@gmail.com","subject":"[PATCH v2 05/27] userdiff: add and use for_each_userdiff_driver()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T15:44:05Z","receivedAt":"2021-02-15T15:49:58Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Refactor the userdiff_find_by_namelen() function so that a new\nfor_each_userdiff_driver() API function does most of the work.\n\nThis will be useful for the same reason we've got other for_each_*()\nAPI functions as part of various APIs, and will be used in a follow-up\ncommit.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n userdiff.c | 61 +++++++++++++++++++++++++++++++++++++++++++-----------\n userdiff.h | 15 ++++++++++++++\n 2 files changed, 64 insertions(+), 12 deletions(-)\n\ndiff --git a/userdiff.c b/userdiff.c\nindex 10a02d3620..55f4f769bd 100644\n--- a/userdiff.c\n+++ b/userdiff.c\n@@ -259,20 +259,32 @@ static struct userdiff_driver driver_false = {\n \t{ NULL, 0 }\n };\n \n-static struct userdiff_driver *userdiff_find_by_namelen(const char *k, size_t len)\n+struct for_each_userdiff_driver_cb {\n+\tconst char *k;\n+\tsize_t len;\n+\tstruct userdiff_driver *driver;\n+};\n+\n+static int userdiff_find_by_namelen_cb(struct userdiff_driver *driver,\n+\t\t\t\t       enum userdiff_driver_type type, void *priv)\n {\n-\tint i;\n-\tfor (i = 0; i < ndrivers; i++) {\n-\t\tstruct userdiff_driver *drv = drivers + i;\n-\t\tif (!strncmp(drv->name, k, len) && !drv->name[len])\n-\t\t\treturn drv;\n-\t}\n-\tfor (i = 0; i < ARRAY_SIZE(builtin_drivers); i++) {\n-\t\tstruct userdiff_driver *drv = builtin_drivers + i;\n-\t\tif (!strncmp(drv->name, k, len) && !drv->name[len])\n-\t\t\treturn drv;\n+\tstruct for_each_userdiff_driver_cb *cb_data = priv;\n+\n+\tif (!strncmp(driver->name, cb_data->k, cb_data->len) &&\n+\t    !driver->name[cb_data->len]) {\n+\t\tcb_data->driver = driver;\n+\t\treturn -1; /* found it! */\n \t}\n-\treturn NULL;\n+\treturn 0;\n+}\n+\n+static struct userdiff_driver *userdiff_find_by_namelen(const char *k, size_t len)\n+{\n+\tstruct for_each_userdiff_driver_cb udcbdata = { .k = k, .len = len, .driver = NULL };\n+\n+\tfor_each_userdiff_driver(userdiff_find_by_namelen_cb,\n+\t\t\t\t USERDIFF_DRIVER_TYPE_UNSPECIFIED, &udcbdata);\n+\treturn udcbdata.driver;\n }\n \n static int parse_funcname(struct userdiff_funcname *f, const char *k,\n@@ -373,3 +385,28 @@ struct userdiff_driver *userdiff_get_textconv(struct repository *r,\n \n \treturn driver;\n }\n+\n+int for_each_userdiff_driver(each_userdiff_driver_fn fn,\n+\t\t\t     enum userdiff_driver_type type, void *cb_data)\n+{\n+\tint i, ret;\n+\tif (type & (USERDIFF_DRIVER_TYPE_UNSPECIFIED | USERDIFF_DRIVER_TYPE_CUSTOM)) {\n+\n+\t\tfor (i = 0; i < ndrivers; i++) {\n+\t\t\tstruct userdiff_driver *drv = drivers + i;\n+\t\t\tret = fn(drv, USERDIFF_DRIVER_TYPE_CUSTOM, cb_data);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\t\t}\n+\t}\n+\tif (type & (USERDIFF_DRIVER_TYPE_UNSPECIFIED | USERDIFF_DRIVER_TYPE_BUILTIN)) {\n+\n+\t\tfor (i = 0; i < ARRAY_SIZE(builtin_drivers); i++) {\n+\t\t\tstruct userdiff_driver *drv = builtin_drivers + i;\n+\t\t\tret = fn(drv, USERDIFF_DRIVER_TYPE_BUILTIN, cb_data);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\t\t}\n+\t}\n+\treturn 0;\n+}\ndiff --git a/userdiff.h b/userdiff.h\nindex 203057e13e..fe14014a77 100644\n--- a/userdiff.h\n+++ b/userdiff.h\n@@ -21,6 +21,13 @@ struct userdiff_driver {\n \tstruct notes_cache *textconv_cache;\n \tint textconv_want_cache;\n };\n+enum userdiff_driver_type {\n+\tUSERDIFF_DRIVER_TYPE_UNSPECIFIED = 1<<0,\n+\tUSERDIFF_DRIVER_TYPE_BUILTIN = 1<<1,\n+\tUSERDIFF_DRIVER_TYPE_CUSTOM = 1<<2,\n+};\n+typedef int (*each_userdiff_driver_fn)(struct userdiff_driver *,\n+\t\t\t\t       enum userdiff_driver_type, void *);\n \n int userdiff_config(const char *k, const char *v);\n struct userdiff_driver *userdiff_find_by_name(const char *name);\n@@ -34,4 +41,12 @@ struct userdiff_driver *userdiff_find_by_path(struct index_state *istate,\n struct userdiff_driver *userdiff_get_textconv(struct repository *r,\n \t\t\t\t\t      struct userdiff_driver *driver);\n \n+/*\n+ * Iterate over each driver of type userdiff_driver_type, or\n+ * USERDIFF_DRIVER_TYPE_UNSPECIFIED for all of them. Return non-zero\n+ * to exit from the loop.\n+ */\n+int for_each_userdiff_driver(each_userdiff_driver_fn,\n+\t\t\t     enum userdiff_driver_type, void *);\n+\n #endif /* USERDIFF */\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416923","messageId":"20210215154427.32693-4-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215005236.11313-1-avarab@gmail.com","subject":"[PATCH v2 03/27] userdiff style: declare patterns with consistent style","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T15:44:03Z","receivedAt":"2021-02-15T15:49:58Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change those patterns which were declared with a regex on the same\nline as the \"PATTERNS()\" line to put that regex on the next line, and\nadd missing \"/* -- */\" separator comments between the pattern and\nword_regex.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n userdiff.c | 20 +++++++++++++++-----\n 1 file changed, 15 insertions(+), 5 deletions(-)\n\ndiff --git a/userdiff.c b/userdiff.c\nindex c92cbcc054..c7aaf7094f 100644\n--- a/userdiff.c\n+++ b/userdiff.c\n@@ -44,7 +44,9 @@ PATTERNS(\"bash\",\n \t /* -- */\n \t /* Characters not in the default $IFS value */\n \t \"[^ \\t]+\"),\n-PATTERNS(\"bibtex\", \"(@[a-zA-Z]{1,}[ \\t]*\\\\{{0,1}[ \\t]*[^ \\t\\\"@',\\\\#}{~%]*).*$\",\n+PATTERNS(\"bibtex\",\n+\t \"(@[a-zA-Z]{1,}[ \\t]*\\\\{{0,1}[ \\t]*[^ \\t\\\"@',\\\\#}{~%]*).*$\",\n+\t /* -- */\n \t \"[={}\\\"]|[^={}\\\" \\t]+\"),\n PATTERNS(\"cpp\",\n \t /* Jump targets or access declarations */\n@@ -121,7 +123,9 @@ IPATTERN(\"fortran\",\n \t  * they would have been matched above as a variable anyway. */\n \t \"|[-+]?[0-9.]+([AaIiDdEeFfLlTtXx][Ss]?[-+]?[0-9.]*)?(_[a-zA-Z0-9][a-zA-Z0-9_]*)?\"\n \t \"|//|\\\\*\\\\*|::|[/<>=]=\"),\n-IPATTERN(\"fountain\", \"^((\\\\.[^.]|(int|ext|est|int\\\\.?/ext|i/e)[. ]).*)$\",\n+IPATTERN(\"fountain\",\n+\t \"^((\\\\.[^.]|(int|ext|est|int\\\\.?/ext|i/e)[. ]).*)$\",\n+\t /* -- */\n \t \"[^ \\t-]+\"),\n PATTERNS(\"golang\",\n \t /* Functions */\n@@ -132,7 +136,9 @@ PATTERNS(\"golang\",\n \t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n \t \"|[-+0-9.eE]+i?|0[xX]?[0-9a-fA-F]+i?\"\n \t \"|[-+*/<>%&^|=!:]=|--|\\\\+\\\\+|<<=?|>>=?|&\\\\^=?|&&|\\\\|\\\\||<-|\\\\.{3}\"),\n-PATTERNS(\"html\", \"^[ \\t]*(<[Hh][1-6]([ \\t].*)?>.*)$\",\n+PATTERNS(\"html\",\n+\t \"^[ \\t]*(<[Hh][1-6]([ \\t].*)?>.*)$\",\n+\t /* -- */\n \t \"[^<>= \\t]+\"),\n PATTERNS(\"java\",\n \t \"!^[ \\t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)\\n\"\n@@ -144,6 +150,7 @@ PATTERNS(\"java\",\n \t \"|--|\\\\+\\\\+|<<=?|>>>?=?|&&|\\\\|\\\\|\"),\n PATTERNS(\"markdown\",\n \t \"^ {0,3}#{1,6}[ \\t].*\",\n+\t /* -- */\n \t \"[^<>= \\t]+\"),\n PATTERNS(\"matlab\",\n \t /*\n@@ -152,6 +159,7 @@ PATTERNS(\"matlab\",\n \t  * that is understood by both.\n \t  */\n \t \"^[[:space:]]*((classdef|function)[[:space:]].*)$|^(%%%?|##)[[:space:]].*$\",\n+\t /* -- */\n \t \"[a-zA-Z_][a-zA-Z0-9_]*|[-+0-9.e]+|[=~<>]=|\\\\.[*/\\\\^']|\\\\|\\\\||&&\"),\n PATTERNS(\"objc\",\n \t /* Negate C statements that can look like functions */\n@@ -212,13 +220,15 @@ PATTERNS(\"php\",\n \t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n \t \"|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+\"\n \t \"|[-+*/<>%&^|=!.]=|--|\\\\+\\\\+|<<=?|>>=?|===|&&|\\\\|\\\\||::|->\"),\n-PATTERNS(\"python\", \"^[ \\t]*((class|(async[ \\t]+)?def)[ \\t].*)$\",\n+PATTERNS(\"python\",\n+\t \"^[ \\t]*((class|(async[ \\t]+)?def)[ \\t].*)$\",\n \t /* -- */\n \t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n \t \"|[-+0-9.e]+[jJlL]?|0[xX]?[0-9a-fA-F]+[lL]?\"\n \t \"|[-+*/<>%&^|=!]=|//=?|<<=?|>>=?|\\\\*\\\\*=?\"),\n \t /* -- */\n-PATTERNS(\"ruby\", \"^[ \\t]*((class|module|def)[ \\t].*)$\",\n+PATTERNS(\"ruby\",\n+\t \"^[ \\t]*((class|module|def)[ \\t].*)$\",\n \t /* -- */\n \t \"(@|@@|\\\\$)?[a-zA-Z_][a-zA-Z0-9_]*\"\n \t \"|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+|\\\\?(\\\\\\\\C-)?(\\\\\\\\M-)?.\"\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416924","messageId":"20210215154427.32693-14-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215005236.11313-1-avarab@gmail.com","subject":"[PATCH v2 13/27] userdiff tests: do config teardown in test_diff_funcname()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T15:44:13Z","receivedAt":"2021-02-15T15:50:19Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Do a teardown of any custom \"diff.<what>.x?funcname\" config after a\ntest_diff_funcname() test runs. Nothing currently uses this, but a\nfollow-up commit will start setting custom config before certain\ntests. Centralizing this teardown makes the tests simpler.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018-diff-funcname.sh | 7 +++++++\n 1 file changed, 7 insertions(+)\n\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex decf7961f9..4cb0b7ba2b 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -78,6 +78,13 @@ test_diff_funcname () {\n \t\tgit diff -U1 \"$what\" >diff &&\n \t\tsed -n -e \"s/^.*@@$//p\" -e \"s/^.*@@ //p\" <diff >actual &&\n \t\ttest_cmp expected actual\n+\t' &&\n+\n+\ttest_expect_success \"teardown: $desc\" '\n+\t\t# In case any custom config was set immediately before\n+\t\t# the test itself in the test file\n+\t\ttest_unconfig \"diff.$what.funcname\" &&\n+\t\ttest_unconfig \"diff.$what.xfuncname\"\n \t'\n }\n \n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416925","messageId":"20210215154427.32693-10-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215005236.11313-1-avarab@gmail.com","subject":"[PATCH v2 09/27] userdiff tests: match full hunk headers","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T15:44:09Z","receivedAt":"2021-02-15T15:50:25Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a regression in the test framework for userdiff added in\nbfa7d01413 (t4018: an infrastructure to test hunk headers,\n2014-03-21).\n\nThe testing infrastructure added in that change went overboard with\nsimplifying the tests, to the point where we lost test coverage.\n\nBefore that we'd been able to test the full context line, or ever\nsince the feature was originally added in f258475a6e (Per-path\nattribute based hunk header selection., 2007-07-06).\n\nAfter bfa7d01413 all we cared about was whether \"RIGHT\" appeared on\nthe line. We thus lost the information about whether or not \"RIGHT\"\nwas extracted from the line for the hunk header, or the line appeared\nin full (or other subset of the line).\n\nLet's bring back coverage for that by adding corresponding *.ctx\nfiles, this has the added advantage that we're doing a \"test_cmp\", so\nwhen we have failures it's just a non-zero exit code from \"grep\",\nwe'll actually have something meaningful in the \"-v\" output.\n\nAs we'll see in a follow-up commit this is an intermediate step\ntowards even better test coverage. I'm structuring these tests in such\na way as to benefit from the diff.colorMove detection.\n\nThe \"sed -n -e\" here was originally a single 's/^.*@@\\( \\|$\\)//p'\npattern, but the '\\( \\|$\\)' part had portability issues on OSX and\nAIX.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018-diff-funcname.sh                      |  7 +++---\n t/t4018/README                                | 22 +++++++++----------\n t/t4018/README.ctx                            |  1 +\n t/t4018/bash-arithmetic-function.ctx          |  1 +\n t/t4018/bash-bashism-style-compact.ctx        |  1 +\n t/t4018/bash-bashism-style-function.ctx       |  1 +\n t/t4018/bash-bashism-style-whitespace.ctx     |  1 +\n t/t4018/bash-conditional-function.ctx         |  1 +\n t/t4018/bash-missing-parentheses.ctx          |  1 +\n t/t4018/bash-mixed-style-compact.ctx          |  1 +\n t/t4018/bash-mixed-style-function.ctx         |  1 +\n t/t4018/bash-nested-functions.ctx             |  1 +\n t/t4018/bash-other-characters.ctx             |  1 +\n t/t4018/bash-posix-style-compact.ctx          |  1 +\n t/t4018/bash-posix-style-function.ctx         |  1 +\n t/t4018/bash-posix-style-whitespace.ctx       |  1 +\n t/t4018/bash-subshell-function.ctx            |  1 +\n t/t4018/bash-trailing-comment.ctx             |  1 +\n t/t4018/cpp-c++-function.ctx                  |  1 +\n t/t4018/cpp-class-constructor-mem-init.ctx    |  1 +\n t/t4018/cpp-class-constructor.ctx             |  1 +\n t/t4018/cpp-class-definition-derived.ctx      |  1 +\n t/t4018/cpp-class-definition.ctx              |  1 +\n t/t4018/cpp-class-destructor.ctx              |  1 +\n .../cpp-function-returning-global-type.ctx    |  1 +\n t/t4018/cpp-function-returning-nested.ctx     |  1 +\n t/t4018/cpp-function-returning-pointer.ctx    |  1 +\n t/t4018/cpp-function-returning-reference.ctx  |  1 +\n t/t4018/cpp-gnu-style-function.ctx            |  1 +\n t/t4018/cpp-namespace-definition.ctx          |  1 +\n t/t4018/cpp-operator-definition.ctx           |  1 +\n t/t4018/cpp-skip-access-specifiers.ctx        |  1 +\n t/t4018/cpp-skip-comment-block.ctx            |  1 +\n t/t4018/cpp-skip-labels.ctx                   |  1 +\n t/t4018/cpp-struct-definition.ctx             |  1 +\n t/t4018/cpp-struct-single-line.ctx            |  1 +\n t/t4018/cpp-template-function-definition.ctx  |  1 +\n t/t4018/cpp-union-definition.ctx              |  1 +\n t/t4018/cpp-void-c-function.ctx               |  1 +\n t/t4018/css-attribute-value-selector.ctx      |  1 +\n t/t4018/css-block-level-@-statements.ctx      |  1 +\n t/t4018/css-brace-in-col-1.ctx                |  1 +\n t/t4018/css-class-selector.ctx                |  1 +\n t/t4018/css-colon-eol.ctx                     |  1 +\n t/t4018/css-colon-selector.ctx                |  1 +\n t/t4018/css-common.ctx                        |  1 +\n t/t4018/css-id-selector.ctx                   |  1 +\n t/t4018/css-long-selector-list.ctx            |  1 +\n t/t4018/css-prop-sans-indent.ctx              |  1 +\n t/t4018/css-root-selector.ctx                 |  1 +\n t/t4018/css-short-selector-list.ctx           |  1 +\n t/t4018/css-trailing-space.ctx                |  1 +\n t/t4018/custom1-pattern.ctx                   |  1 +\n t/t4018/custom2-match-to-end-of-line.ctx      |  1 +\n t/t4018/custom3-alternation-in-pattern.ctx    |  1 +\n t/t4018/dts-labels.ctx                        |  1 +\n t/t4018/dts-node-unitless.ctx                 |  1 +\n t/t4018/dts-nodes-boolean-prop.ctx            |  1 +\n t/t4018/dts-nodes-comment1.ctx                |  1 +\n t/t4018/dts-nodes-comment2.ctx                |  1 +\n t/t4018/dts-nodes-multiline-prop.ctx          |  1 +\n t/t4018/dts-nodes.ctx                         |  1 +\n t/t4018/dts-reference.ctx                     |  1 +\n t/t4018/dts-root-comment.ctx                  |  1 +\n t/t4018/dts-root.ctx                          |  1 +\n t/t4018/elixir-do-not-pick-end.ctx            |  1 +\n t/t4018/elixir-ex-unit-test.ctx               |  1 +\n t/t4018/elixir-function.ctx                   |  1 +\n t/t4018/elixir-macro.ctx                      |  1 +\n t/t4018/elixir-module-func.ctx                |  1 +\n t/t4018/elixir-module.ctx                     |  1 +\n t/t4018/elixir-nested-module.ctx              |  1 +\n t/t4018/elixir-private-function.ctx           |  1 +\n t/t4018/elixir-protocol-implementation.ctx    |  1 +\n t/t4018/elixir-protocol.ctx                   |  1 +\n t/t4018/fortran-block-data.ctx                |  1 +\n t/t4018/fortran-comment-keyword.ctx           |  1 +\n t/t4018/fortran-comment-legacy-star.ctx       |  1 +\n t/t4018/fortran-comment-legacy.ctx            |  1 +\n t/t4018/fortran-comment.ctx                   |  1 +\n t/t4018/fortran-external-function.ctx         |  1 +\n t/t4018/fortran-external-subroutine.ctx       |  1 +\n t/t4018/fortran-module-procedure.ctx          |  1 +\n t/t4018/fortran-module.ctx                    |  1 +\n t/t4018/fortran-program.ctx                   |  1 +\n t/t4018/fountain-scene.ctx                    |  1 +\n t/t4018/golang-complex-function.ctx           |  1 +\n t/t4018/golang-func.ctx                       |  1 +\n t/t4018/golang-interface.ctx                  |  1 +\n t/t4018/golang-long-func.ctx                  |  1 +\n t/t4018/golang-struct.ctx                     |  1 +\n t/t4018/java-class-member-function.ctx        |  1 +\n t/t4018/markdown-heading-indented.ctx         |  1 +\n t/t4018/markdown-heading-non-headings.ctx     |  1 +\n t/t4018/matlab-class-definition.ctx           |  1 +\n t/t4018/matlab-function.ctx                   |  1 +\n t/t4018/matlab-octave-section-1.ctx           |  1 +\n t/t4018/matlab-octave-section-2.ctx           |  1 +\n t/t4018/matlab-section.ctx                    |  1 +\n t/t4018/perl-skip-end-of-heredoc.ctx          |  1 +\n t/t4018/perl-skip-forward-decl.ctx            |  1 +\n t/t4018/perl-skip-sub-in-pod.ctx              |  1 +\n t/t4018/perl-sub-definition-kr-brace.ctx      |  1 +\n t/t4018/perl-sub-definition.ctx               |  1 +\n t/t4018/php-abstract-class.ctx                |  1 +\n t/t4018/php-abstract-method.ctx               |  1 +\n t/t4018/php-class.ctx                         |  1 +\n t/t4018/php-final-class.ctx                   |  1 +\n t/t4018/php-final-method.ctx                  |  1 +\n t/t4018/php-function.ctx                      |  1 +\n t/t4018/php-interface.ctx                     |  1 +\n t/t4018/php-method.ctx                        |  1 +\n t/t4018/php-trait.ctx                         |  1 +\n t/t4018/python-async-def.ctx                  |  1 +\n t/t4018/python-class.ctx                      |  1 +\n t/t4018/python-def.ctx                        |  1 +\n t/t4018/python-indented-async-def.ctx         |  1 +\n t/t4018/python-indented-class.ctx             |  1 +\n t/t4018/python-indented-def.ctx               |  1 +\n t/t4018/rust-fn.ctx                           |  1 +\n t/t4018/rust-impl.ctx                         |  1 +\n t/t4018/rust-macro-rules.ctx                  |  1 +\n t/t4018/rust-struct.ctx                       |  1 +\n t/t4018/rust-trait.ctx                        |  1 +\n 124 files changed, 137 insertions(+), 14 deletions(-)\n create mode 100644 t/t4018/README.ctx\n create mode 100644 t/t4018/bash-arithmetic-function.ctx\n create mode 100644 t/t4018/bash-bashism-style-compact.ctx\n create mode 100644 t/t4018/bash-bashism-style-function.ctx\n create mode 100644 t/t4018/bash-bashism-style-whitespace.ctx\n create mode 100644 t/t4018/bash-conditional-function.ctx\n create mode 100644 t/t4018/bash-missing-parentheses.ctx\n create mode 100644 t/t4018/bash-mixed-style-compact.ctx\n create mode 100644 t/t4018/bash-mixed-style-function.ctx\n create mode 100644 t/t4018/bash-nested-functions.ctx\n create mode 100644 t/t4018/bash-other-characters.ctx\n create mode 100644 t/t4018/bash-posix-style-compact.ctx\n create mode 100644 t/t4018/bash-posix-style-function.ctx\n create mode 100644 t/t4018/bash-posix-style-whitespace.ctx\n create mode 100644 t/t4018/bash-subshell-function.ctx\n create mode 100644 t/t4018/bash-trailing-comment.ctx\n create mode 100644 t/t4018/cpp-c++-function.ctx\n create mode 100644 t/t4018/cpp-class-constructor-mem-init.ctx\n create mode 100644 t/t4018/cpp-class-constructor.ctx\n create mode 100644 t/t4018/cpp-class-definition-derived.ctx\n create mode 100644 t/t4018/cpp-class-definition.ctx\n create mode 100644 t/t4018/cpp-class-destructor.ctx\n create mode 100644 t/t4018/cpp-function-returning-global-type.ctx\n create mode 100644 t/t4018/cpp-function-returning-nested.ctx\n create mode 100644 t/t4018/cpp-function-returning-pointer.ctx\n create mode 100644 t/t4018/cpp-function-returning-reference.ctx\n create mode 100644 t/t4018/cpp-gnu-style-function.ctx\n create mode 100644 t/t4018/cpp-namespace-definition.ctx\n create mode 100644 t/t4018/cpp-operator-definition.ctx\n create mode 100644 t/t4018/cpp-skip-access-specifiers.ctx\n create mode 100644 t/t4018/cpp-skip-comment-block.ctx\n create mode 100644 t/t4018/cpp-skip-labels.ctx\n create mode 100644 t/t4018/cpp-struct-definition.ctx\n create mode 100644 t/t4018/cpp-struct-single-line.ctx\n create mode 100644 t/t4018/cpp-template-function-definition.ctx\n create mode 100644 t/t4018/cpp-union-definition.ctx\n create mode 100644 t/t4018/cpp-void-c-function.ctx\n create mode 100644 t/t4018/css-attribute-value-selector.ctx\n create mode 100644 t/t4018/css-block-level-@-statements.ctx\n create mode 100644 t/t4018/css-brace-in-col-1.ctx\n create mode 100644 t/t4018/css-class-selector.ctx\n create mode 100644 t/t4018/css-colon-eol.ctx\n create mode 100644 t/t4018/css-colon-selector.ctx\n create mode 100644 t/t4018/css-common.ctx\n create mode 100644 t/t4018/css-id-selector.ctx\n create mode 100644 t/t4018/css-long-selector-list.ctx\n create mode 100644 t/t4018/css-prop-sans-indent.ctx\n create mode 100644 t/t4018/css-root-selector.ctx\n create mode 100644 t/t4018/css-short-selector-list.ctx\n create mode 100644 t/t4018/css-trailing-space.ctx\n create mode 100644 t/t4018/custom1-pattern.ctx\n create mode 100644 t/t4018/custom2-match-to-end-of-line.ctx\n create mode 100644 t/t4018/custom3-alternation-in-pattern.ctx\n create mode 100644 t/t4018/dts-labels.ctx\n create mode 100644 t/t4018/dts-node-unitless.ctx\n create mode 100644 t/t4018/dts-nodes-boolean-prop.ctx\n create mode 100644 t/t4018/dts-nodes-comment1.ctx\n create mode 100644 t/t4018/dts-nodes-comment2.ctx\n create mode 100644 t/t4018/dts-nodes-multiline-prop.ctx\n create mode 100644 t/t4018/dts-nodes.ctx\n create mode 100644 t/t4018/dts-reference.ctx\n create mode 100644 t/t4018/dts-root-comment.ctx\n create mode 100644 t/t4018/dts-root.ctx\n create mode 100644 t/t4018/elixir-do-not-pick-end.ctx\n create mode 100644 t/t4018/elixir-ex-unit-test.ctx\n create mode 100644 t/t4018/elixir-function.ctx\n create mode 100644 t/t4018/elixir-macro.ctx\n create mode 100644 t/t4018/elixir-module-func.ctx\n create mode 100644 t/t4018/elixir-module.ctx\n create mode 100644 t/t4018/elixir-nested-module.ctx\n create mode 100644 t/t4018/elixir-private-function.ctx\n create mode 100644 t/t4018/elixir-protocol-implementation.ctx\n create mode 100644 t/t4018/elixir-protocol.ctx\n create mode 100644 t/t4018/fortran-block-data.ctx\n create mode 100644 t/t4018/fortran-comment-keyword.ctx\n create mode 100644 t/t4018/fortran-comment-legacy-star.ctx\n create mode 100644 t/t4018/fortran-comment-legacy.ctx\n create mode 100644 t/t4018/fortran-comment.ctx\n create mode 100644 t/t4018/fortran-external-function.ctx\n create mode 100644 t/t4018/fortran-external-subroutine.ctx\n create mode 100644 t/t4018/fortran-module-procedure.ctx\n create mode 100644 t/t4018/fortran-module.ctx\n create mode 100644 t/t4018/fortran-program.ctx\n create mode 100644 t/t4018/fountain-scene.ctx\n create mode 100644 t/t4018/golang-complex-function.ctx\n create mode 100644 t/t4018/golang-func.ctx\n create mode 100644 t/t4018/golang-interface.ctx\n create mode 100644 t/t4018/golang-long-func.ctx\n create mode 100644 t/t4018/golang-struct.ctx\n create mode 100644 t/t4018/java-class-member-function.ctx\n create mode 100644 t/t4018/markdown-heading-indented.ctx\n create mode 100644 t/t4018/markdown-heading-non-headings.ctx\n create mode 100644 t/t4018/matlab-class-definition.ctx\n create mode 100644 t/t4018/matlab-function.ctx\n create mode 100644 t/t4018/matlab-octave-section-1.ctx\n create mode 100644 t/t4018/matlab-octave-section-2.ctx\n create mode 100644 t/t4018/matlab-section.ctx\n create mode 100644 t/t4018/perl-skip-end-of-heredoc.ctx\n create mode 100644 t/t4018/perl-skip-forward-decl.ctx\n create mode 100644 t/t4018/perl-skip-sub-in-pod.ctx\n create mode 100644 t/t4018/perl-sub-definition-kr-brace.ctx\n create mode 100644 t/t4018/perl-sub-definition.ctx\n create mode 100644 t/t4018/php-abstract-class.ctx\n create mode 100644 t/t4018/php-abstract-method.ctx\n create mode 100644 t/t4018/php-class.ctx\n create mode 100644 t/t4018/php-final-class.ctx\n create mode 100644 t/t4018/php-final-method.ctx\n create mode 100644 t/t4018/php-function.ctx\n create mode 100644 t/t4018/php-interface.ctx\n create mode 100644 t/t4018/php-method.ctx\n create mode 100644 t/t4018/php-trait.ctx\n create mode 100644 t/t4018/python-async-def.ctx\n create mode 100644 t/t4018/python-class.ctx\n create mode 100644 t/t4018/python-def.ctx\n create mode 100644 t/t4018/python-indented-async-def.ctx\n create mode 100644 t/t4018/python-indented-class.ctx\n create mode 100644 t/t4018/python-indented-def.ctx\n create mode 100644 t/t4018/rust-fn.ctx\n create mode 100644 t/t4018/rust-impl.ctx\n create mode 100644 t/t4018/rust-macro-rules.ctx\n create mode 100644 t/t4018/rust-struct.ctx\n create mode 100644 t/t4018/rust-trait.ctx\n\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex 5994c5b47a..3941316682 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -80,11 +80,12 @@ test_expect_success 'setup hunk header tests' '\n '\n \n # check each individual file\n-for i in $(git ls-files)\n+for i in $(git ls-files -- ':!*.ctx')\n do\n \ttest_expect_success \"hunk header: $i\" \"\n-\t\tgit diff -U1 $i >actual &&\n-\t\tgrep '@@ .* @@.*RIGHT' actual\n+\t\tgit diff -U1 $i >diff &&\n+\t\tsed -n -e 's/^.*@@$//p' -e 's/^.*@@ //p' <diff >ctx &&\n+\t\ttest_cmp $i.ctx ctx\n \t\"\n done\n \ndiff --git a/t/t4018/README b/t/t4018/README\nindex 2d25b2b4fc..d0619f76d4 100644\n--- a/t/t4018/README\n+++ b/t/t4018/README\n@@ -1,15 +1,15 @@\n-How to write RIGHT test cases\n-=============================\n+How to write test cases\n+=======================\n+\n+Create test cases called \"LANG-whatever\" in this directory, where\n+\"LANG\" is e.g. the userdiff driver name, where \"whatever\" is a brief\n+description of the test.\n \n Insert the word \"ChangeMe\" (exactly this form) at a distance of\n at least two lines from the line that must appear in the hunk header.\n \n-The text that must appear in the hunk header must contain the word\n-\"right\", but in all upper-case, like in the title above.\n-\n-This text is a bit twisted and out of order, but it is itself a\n-test case for the default hunk header pattern. Know what you are doing\n-if you change it.\n-\n-BTW, this tests that the head line goes to the hunk header, not the line\n-of equal signs.\n+The text that must appear in the hunk header must contains the word\n+\"RIGHT\" by convention. The \"LANG-whatever.ctx\" file contains what we\n+expect to appear in the hunk header. We munged away the starting \"@@\n+[...] @@\" part of the line for ease of not having to hardcode the line\n+numbers and offsets.\ndiff --git a/t/t4018/README.ctx b/t/t4018/README.ctx\nnew file mode 100644\nindex 0000000000..cd79384b04\n--- /dev/null\n+++ b/t/t4018/README.ctx\n@@ -0,0 +1 @@\n+description of the test.\ndiff --git a/t/t4018/bash-arithmetic-function.ctx b/t/t4018/bash-arithmetic-function.ctx\nnew file mode 100644\nindex 0000000000..811eac7d2f\n--- /dev/null\n+++ b/t/t4018/bash-arithmetic-function.ctx\n@@ -0,0 +1 @@\n+RIGHT()\ndiff --git a/t/t4018/bash-bashism-style-compact.ctx b/t/t4018/bash-bashism-style-compact.ctx\nnew file mode 100644\nindex 0000000000..4f8eac48c6\n--- /dev/null\n+++ b/t/t4018/bash-bashism-style-compact.ctx\n@@ -0,0 +1 @@\n+function RIGHT {\ndiff --git a/t/t4018/bash-bashism-style-function.ctx b/t/t4018/bash-bashism-style-function.ctx\nnew file mode 100644\nindex 0000000000..4f8eac48c6\n--- /dev/null\n+++ b/t/t4018/bash-bashism-style-function.ctx\n@@ -0,0 +1 @@\n+function RIGHT {\ndiff --git a/t/t4018/bash-bashism-style-whitespace.ctx b/t/t4018/bash-bashism-style-whitespace.ctx\nnew file mode 100644\nindex 0000000000..35dbd0220e\n--- /dev/null\n+++ b/t/t4018/bash-bashism-style-whitespace.ctx\n@@ -0,0 +1 @@\n+function \tRIGHT \t( \t) \t{\ndiff --git a/t/t4018/bash-conditional-function.ctx b/t/t4018/bash-conditional-function.ctx\nnew file mode 100644\nindex 0000000000..811eac7d2f\n--- /dev/null\n+++ b/t/t4018/bash-conditional-function.ctx\n@@ -0,0 +1 @@\n+RIGHT()\ndiff --git a/t/t4018/bash-missing-parentheses.ctx b/t/t4018/bash-missing-parentheses.ctx\nnew file mode 100644\nindex 0000000000..4f8eac48c6\n--- /dev/null\n+++ b/t/t4018/bash-missing-parentheses.ctx\n@@ -0,0 +1 @@\n+function RIGHT {\ndiff --git a/t/t4018/bash-mixed-style-compact.ctx b/t/t4018/bash-mixed-style-compact.ctx\nnew file mode 100644\nindex 0000000000..bba11074eb\n--- /dev/null\n+++ b/t/t4018/bash-mixed-style-compact.ctx\n@@ -0,0 +1 @@\n+function RIGHT(){\ndiff --git a/t/t4018/bash-mixed-style-function.ctx b/t/t4018/bash-mixed-style-function.ctx\nnew file mode 100644\nindex 0000000000..922b87a4aa\n--- /dev/null\n+++ b/t/t4018/bash-mixed-style-function.ctx\n@@ -0,0 +1 @@\n+function RIGHT() {\ndiff --git a/t/t4018/bash-nested-functions.ctx b/t/t4018/bash-nested-functions.ctx\nnew file mode 100644\nindex 0000000000..811eac7d2f\n--- /dev/null\n+++ b/t/t4018/bash-nested-functions.ctx\n@@ -0,0 +1 @@\n+RIGHT()\ndiff --git a/t/t4018/bash-other-characters.ctx b/t/t4018/bash-other-characters.ctx\nnew file mode 100644\nindex 0000000000..6a55317fdf\n--- /dev/null\n+++ b/t/t4018/bash-other-characters.ctx\n@@ -0,0 +1 @@\n+_RIGHT_0n()\ndiff --git a/t/t4018/bash-posix-style-compact.ctx b/t/t4018/bash-posix-style-compact.ctx\nnew file mode 100644\nindex 0000000000..811eac7d2f\n--- /dev/null\n+++ b/t/t4018/bash-posix-style-compact.ctx\n@@ -0,0 +1 @@\n+RIGHT()\ndiff --git a/t/t4018/bash-posix-style-function.ctx b/t/t4018/bash-posix-style-function.ctx\nnew file mode 100644\nindex 0000000000..811eac7d2f\n--- /dev/null\n+++ b/t/t4018/bash-posix-style-function.ctx\n@@ -0,0 +1 @@\n+RIGHT()\ndiff --git a/t/t4018/bash-posix-style-whitespace.ctx b/t/t4018/bash-posix-style-whitespace.ctx\nnew file mode 100644\nindex 0000000000..28f8698e14\n--- /dev/null\n+++ b/t/t4018/bash-posix-style-whitespace.ctx\n@@ -0,0 +1 @@\n+RIGHT \t( \t)\ndiff --git a/t/t4018/bash-subshell-function.ctx b/t/t4018/bash-subshell-function.ctx\nnew file mode 100644\nindex 0000000000..811eac7d2f\n--- /dev/null\n+++ b/t/t4018/bash-subshell-function.ctx\n@@ -0,0 +1 @@\n+RIGHT()\ndiff --git a/t/t4018/bash-trailing-comment.ctx b/t/t4018/bash-trailing-comment.ctx\nnew file mode 100644\nindex 0000000000..811eac7d2f\n--- /dev/null\n+++ b/t/t4018/bash-trailing-comment.ctx\n@@ -0,0 +1 @@\n+RIGHT()\ndiff --git a/t/t4018/cpp-c++-function.ctx b/t/t4018/cpp-c++-function.ctx\nnew file mode 100644\nindex 0000000000..337b49dbb3\n--- /dev/null\n+++ b/t/t4018/cpp-c++-function.ctx\n@@ -0,0 +1 @@\n+Item RIGHT::DoSomething( Args with_spaces )\ndiff --git a/t/t4018/cpp-class-constructor-mem-init.ctx b/t/t4018/cpp-class-constructor-mem-init.ctx\nnew file mode 100644\nindex 0000000000..6664b8b3d8\n--- /dev/null\n+++ b/t/t4018/cpp-class-constructor-mem-init.ctx\n@@ -0,0 +1 @@\n+Item::Item(int RIGHT) :\ndiff --git a/t/t4018/cpp-class-constructor.ctx b/t/t4018/cpp-class-constructor.ctx\nnew file mode 100644\nindex 0000000000..2dcadfc0ba\n--- /dev/null\n+++ b/t/t4018/cpp-class-constructor.ctx\n@@ -0,0 +1 @@\n+Item::Item(int RIGHT)\ndiff --git a/t/t4018/cpp-class-definition-derived.ctx b/t/t4018/cpp-class-definition-derived.ctx\nnew file mode 100644\nindex 0000000000..146f0a7b7c\n--- /dev/null\n+++ b/t/t4018/cpp-class-definition-derived.ctx\n@@ -0,0 +1 @@\n+class RIGHT :\ndiff --git a/t/t4018/cpp-class-definition.ctx b/t/t4018/cpp-class-definition.ctx\nnew file mode 100644\nindex 0000000000..54bff816d6\n--- /dev/null\n+++ b/t/t4018/cpp-class-definition.ctx\n@@ -0,0 +1 @@\n+class RIGHT\ndiff --git a/t/t4018/cpp-class-destructor.ctx b/t/t4018/cpp-class-destructor.ctx\nnew file mode 100644\nindex 0000000000..5390c17cf6\n--- /dev/null\n+++ b/t/t4018/cpp-class-destructor.ctx\n@@ -0,0 +1 @@\n+RIGHT::~RIGHT()\ndiff --git a/t/t4018/cpp-function-returning-global-type.ctx b/t/t4018/cpp-function-returning-global-type.ctx\nnew file mode 100644\nindex 0000000000..4dcdde25f4\n--- /dev/null\n+++ b/t/t4018/cpp-function-returning-global-type.ctx\n@@ -0,0 +1 @@\n+::Item get::it::RIGHT()\ndiff --git a/t/t4018/cpp-function-returning-nested.ctx b/t/t4018/cpp-function-returning-nested.ctx\nnew file mode 100644\nindex 0000000000..6ef73c8368\n--- /dev/null\n+++ b/t/t4018/cpp-function-returning-nested.ctx\n@@ -0,0 +1 @@\n+get::Item get::it::RIGHT()\ndiff --git a/t/t4018/cpp-function-returning-pointer.ctx b/t/t4018/cpp-function-returning-pointer.ctx\nnew file mode 100644\nindex 0000000000..bb0acce5c7\n--- /dev/null\n+++ b/t/t4018/cpp-function-returning-pointer.ctx\n@@ -0,0 +1 @@\n+const char *get_it_RIGHT(char *ptr)\ndiff --git a/t/t4018/cpp-function-returning-reference.ctx b/t/t4018/cpp-function-returning-reference.ctx\nnew file mode 100644\nindex 0000000000..76afe381fd\n--- /dev/null\n+++ b/t/t4018/cpp-function-returning-reference.ctx\n@@ -0,0 +1 @@\n+string& get::it::RIGHT(char *ptr)\ndiff --git a/t/t4018/cpp-gnu-style-function.ctx b/t/t4018/cpp-gnu-style-function.ctx\nnew file mode 100644\nindex 0000000000..1858287812\n--- /dev/null\n+++ b/t/t4018/cpp-gnu-style-function.ctx\n@@ -0,0 +1 @@\n+RIGHT(int arg)\ndiff --git a/t/t4018/cpp-namespace-definition.ctx b/t/t4018/cpp-namespace-definition.ctx\nnew file mode 100644\nindex 0000000000..14c29c4638\n--- /dev/null\n+++ b/t/t4018/cpp-namespace-definition.ctx\n@@ -0,0 +1 @@\n+namespace RIGHT\ndiff --git a/t/t4018/cpp-operator-definition.ctx b/t/t4018/cpp-operator-definition.ctx\nnew file mode 100644\nindex 0000000000..5b56778961\n--- /dev/null\n+++ b/t/t4018/cpp-operator-definition.ctx\n@@ -0,0 +1 @@\n+Value operator+(Value LEFT, Value RIGHT)\ndiff --git a/t/t4018/cpp-skip-access-specifiers.ctx b/t/t4018/cpp-skip-access-specifiers.ctx\nnew file mode 100644\nindex 0000000000..075bcd883b\n--- /dev/null\n+++ b/t/t4018/cpp-skip-access-specifiers.ctx\n@@ -0,0 +1 @@\n+class RIGHT : public Baseclass\ndiff --git a/t/t4018/cpp-skip-comment-block.ctx b/t/t4018/cpp-skip-comment-block.ctx\nnew file mode 100644\nindex 0000000000..656c59893d\n--- /dev/null\n+++ b/t/t4018/cpp-skip-comment-block.ctx\n@@ -0,0 +1 @@\n+struct item RIGHT(int i)\ndiff --git a/t/t4018/cpp-skip-labels.ctx b/t/t4018/cpp-skip-labels.ctx\nnew file mode 100644\nindex 0000000000..6b0635f7f7\n--- /dev/null\n+++ b/t/t4018/cpp-skip-labels.ctx\n@@ -0,0 +1 @@\n+void RIGHT (void)\ndiff --git a/t/t4018/cpp-struct-definition.ctx b/t/t4018/cpp-struct-definition.ctx\nnew file mode 100644\nindex 0000000000..48ed893279\n--- /dev/null\n+++ b/t/t4018/cpp-struct-definition.ctx\n@@ -0,0 +1 @@\n+struct RIGHT {\ndiff --git a/t/t4018/cpp-struct-single-line.ctx b/t/t4018/cpp-struct-single-line.ctx\nnew file mode 100644\nindex 0000000000..e3bc9d5017\n--- /dev/null\n+++ b/t/t4018/cpp-struct-single-line.ctx\n@@ -0,0 +1 @@\n+struct RIGHT_iterator_tag {};\ndiff --git a/t/t4018/cpp-template-function-definition.ctx b/t/t4018/cpp-template-function-definition.ctx\nnew file mode 100644\nindex 0000000000..c9da39cf65\n--- /dev/null\n+++ b/t/t4018/cpp-template-function-definition.ctx\n@@ -0,0 +1 @@\n+template<class T> int RIGHT(T arg)\ndiff --git a/t/t4018/cpp-union-definition.ctx b/t/t4018/cpp-union-definition.ctx\nnew file mode 100644\nindex 0000000000..2fc7b54fb8\n--- /dev/null\n+++ b/t/t4018/cpp-union-definition.ctx\n@@ -0,0 +1 @@\n+union RIGHT {\ndiff --git a/t/t4018/cpp-void-c-function.ctx b/t/t4018/cpp-void-c-function.ctx\nnew file mode 100644\nindex 0000000000..6b0635f7f7\n--- /dev/null\n+++ b/t/t4018/cpp-void-c-function.ctx\n@@ -0,0 +1 @@\n+void RIGHT (void)\ndiff --git a/t/t4018/css-attribute-value-selector.ctx b/t/t4018/css-attribute-value-selector.ctx\nnew file mode 100644\nindex 0000000000..7f8956251c\n--- /dev/null\n+++ b/t/t4018/css-attribute-value-selector.ctx\n@@ -0,0 +1 @@\n+[class*=\"RIGHT\"] {\ndiff --git a/t/t4018/css-block-level-@-statements.ctx b/t/t4018/css-block-level-@-statements.ctx\nnew file mode 100644\nindex 0000000000..7f5e90468c\n--- /dev/null\n+++ b/t/t4018/css-block-level-@-statements.ctx\n@@ -0,0 +1 @@\n+@keyframes RIGHT {\ndiff --git a/t/t4018/css-brace-in-col-1.ctx b/t/t4018/css-brace-in-col-1.ctx\nnew file mode 100644\nindex 0000000000..91a9105c6a\n--- /dev/null\n+++ b/t/t4018/css-brace-in-col-1.ctx\n@@ -0,0 +1 @@\n+RIGHT label.control-label\ndiff --git a/t/t4018/css-class-selector.ctx b/t/t4018/css-class-selector.ctx\nnew file mode 100644\nindex 0000000000..ac7367d7f4\n--- /dev/null\n+++ b/t/t4018/css-class-selector.ctx\n@@ -0,0 +1 @@\n+.RIGHT {\ndiff --git a/t/t4018/css-colon-eol.ctx b/t/t4018/css-colon-eol.ctx\nnew file mode 100644\nindex 0000000000..b68493b9b0\n--- /dev/null\n+++ b/t/t4018/css-colon-eol.ctx\n@@ -0,0 +1 @@\n+RIGHT h1 {\ndiff --git a/t/t4018/css-colon-selector.ctx b/t/t4018/css-colon-selector.ctx\nnew file mode 100644\nindex 0000000000..00b1a5aefe\n--- /dev/null\n+++ b/t/t4018/css-colon-selector.ctx\n@@ -0,0 +1 @@\n+RIGHT a:hover {\ndiff --git a/t/t4018/css-common.ctx b/t/t4018/css-common.ctx\nnew file mode 100644\nindex 0000000000..43686b4081\n--- /dev/null\n+++ b/t/t4018/css-common.ctx\n@@ -0,0 +1 @@\n+RIGHT label.control-label {\ndiff --git a/t/t4018/css-id-selector.ctx b/t/t4018/css-id-selector.ctx\nnew file mode 100644\nindex 0000000000..ce19f6d8dc\n--- /dev/null\n+++ b/t/t4018/css-id-selector.ctx\n@@ -0,0 +1 @@\n+#RIGHT {\ndiff --git a/t/t4018/css-long-selector-list.ctx b/t/t4018/css-long-selector-list.ctx\nnew file mode 100644\nindex 0000000000..bc8d0fb62c\n--- /dev/null\n+++ b/t/t4018/css-long-selector-list.ctx\n@@ -0,0 +1 @@\n+div ul#RIGHT {\ndiff --git a/t/t4018/css-prop-sans-indent.ctx b/t/t4018/css-prop-sans-indent.ctx\nnew file mode 100644\nindex 0000000000..cc880b2f44\n--- /dev/null\n+++ b/t/t4018/css-prop-sans-indent.ctx\n@@ -0,0 +1 @@\n+RIGHT, label.control-label {\ndiff --git a/t/t4018/css-root-selector.ctx b/t/t4018/css-root-selector.ctx\nnew file mode 100644\nindex 0000000000..3010cded2a\n--- /dev/null\n+++ b/t/t4018/css-root-selector.ctx\n@@ -0,0 +1 @@\n+:RIGHT {\ndiff --git a/t/t4018/css-short-selector-list.ctx b/t/t4018/css-short-selector-list.ctx\nnew file mode 100644\nindex 0000000000..9e5d87d126\n--- /dev/null\n+++ b/t/t4018/css-short-selector-list.ctx\n@@ -0,0 +1 @@\n+label.control, div ul#RIGHT {\ndiff --git a/t/t4018/css-trailing-space.ctx b/t/t4018/css-trailing-space.ctx\nnew file mode 100644\nindex 0000000000..43686b4081\n--- /dev/null\n+++ b/t/t4018/css-trailing-space.ctx\n@@ -0,0 +1 @@\n+RIGHT label.control-label {\ndiff --git a/t/t4018/custom1-pattern.ctx b/t/t4018/custom1-pattern.ctx\nnew file mode 100644\nindex 0000000000..d1609cc9a6\n--- /dev/null\n+++ b/t/t4018/custom1-pattern.ctx\n@@ -0,0 +1 @@\n+int special, RIGHT;\ndiff --git a/t/t4018/custom2-match-to-end-of-line.ctx b/t/t4018/custom2-match-to-end-of-line.ctx\nnew file mode 100644\nindex 0000000000..8294c6e49b\n--- /dev/null\n+++ b/t/t4018/custom2-match-to-end-of-line.ctx\n@@ -0,0 +1 @@\n+RIGHT_Beer\ndiff --git a/t/t4018/custom3-alternation-in-pattern.ctx b/t/t4018/custom3-alternation-in-pattern.ctx\nnew file mode 100644\nindex 0000000000..2125474b68\n--- /dev/null\n+++ b/t/t4018/custom3-alternation-in-pattern.ctx\n@@ -0,0 +1 @@\n+public static void main(String RIGHT[])\ndiff --git a/t/t4018/dts-labels.ctx b/t/t4018/dts-labels.ctx\nnew file mode 100644\nindex 0000000000..48d9373cab\n--- /dev/null\n+++ b/t/t4018/dts-labels.ctx\n@@ -0,0 +1 @@\n+label2: RIGHT {\ndiff --git a/t/t4018/dts-node-unitless.ctx b/t/t4018/dts-node-unitless.ctx\nnew file mode 100644\nindex 0000000000..82c8683fa1\n--- /dev/null\n+++ b/t/t4018/dts-node-unitless.ctx\n@@ -0,0 +1 @@\n+RIGHT {\ndiff --git a/t/t4018/dts-nodes-boolean-prop.ctx b/t/t4018/dts-nodes-boolean-prop.ctx\nnew file mode 100644\nindex 0000000000..3a0232d55d\n--- /dev/null\n+++ b/t/t4018/dts-nodes-boolean-prop.ctx\n@@ -0,0 +1 @@\n+RIGHT@deadf00,4000 {\ndiff --git a/t/t4018/dts-nodes-comment1.ctx b/t/t4018/dts-nodes-comment1.ctx\nnew file mode 100644\nindex 0000000000..ec364600b1\n--- /dev/null\n+++ b/t/t4018/dts-nodes-comment1.ctx\n@@ -0,0 +1 @@\n+RIGHT@deadf00,4000 /* &a comment */ {\ndiff --git a/t/t4018/dts-nodes-comment2.ctx b/t/t4018/dts-nodes-comment2.ctx\nnew file mode 100644\nindex 0000000000..75f0d75258\n--- /dev/null\n+++ b/t/t4018/dts-nodes-comment2.ctx\n@@ -0,0 +1 @@\n+RIGHT@deadf00,4000 { /* a trailing comment */\ndiff --git a/t/t4018/dts-nodes-multiline-prop.ctx b/t/t4018/dts-nodes-multiline-prop.ctx\nnew file mode 100644\nindex 0000000000..3a0232d55d\n--- /dev/null\n+++ b/t/t4018/dts-nodes-multiline-prop.ctx\n@@ -0,0 +1 @@\n+RIGHT@deadf00,4000 {\ndiff --git a/t/t4018/dts-nodes.ctx b/t/t4018/dts-nodes.ctx\nnew file mode 100644\nindex 0000000000..3a0232d55d\n--- /dev/null\n+++ b/t/t4018/dts-nodes.ctx\n@@ -0,0 +1 @@\n+RIGHT@deadf00,4000 {\ndiff --git a/t/t4018/dts-reference.ctx b/t/t4018/dts-reference.ctx\nnew file mode 100644\nindex 0000000000..c1e13409ee\n--- /dev/null\n+++ b/t/t4018/dts-reference.ctx\n@@ -0,0 +1 @@\n+&RIGHT {\ndiff --git a/t/t4018/dts-root-comment.ctx b/t/t4018/dts-root-comment.ctx\nnew file mode 100644\nindex 0000000000..656053dd42\n--- /dev/null\n+++ b/t/t4018/dts-root-comment.ctx\n@@ -0,0 +1 @@\n+/ { RIGHT /* Technically just supposed to be a slash and brace */\ndiff --git a/t/t4018/dts-root.ctx b/t/t4018/dts-root.ctx\nnew file mode 100644\nindex 0000000000..656053dd42\n--- /dev/null\n+++ b/t/t4018/dts-root.ctx\n@@ -0,0 +1 @@\n+/ { RIGHT /* Technically just supposed to be a slash and brace */\ndiff --git a/t/t4018/elixir-do-not-pick-end.ctx b/t/t4018/elixir-do-not-pick-end.ctx\nnew file mode 100644\nindex 0000000000..8f28a7a689\n--- /dev/null\n+++ b/t/t4018/elixir-do-not-pick-end.ctx\n@@ -0,0 +1 @@\n+defmodule RIGHT do\ndiff --git a/t/t4018/elixir-ex-unit-test.ctx b/t/t4018/elixir-ex-unit-test.ctx\nnew file mode 100644\nindex 0000000000..a55e3de2cc\n--- /dev/null\n+++ b/t/t4018/elixir-ex-unit-test.ctx\n@@ -0,0 +1 @@\n+test \"RIGHT\" do\ndiff --git a/t/t4018/elixir-function.ctx b/t/t4018/elixir-function.ctx\nnew file mode 100644\nindex 0000000000..62aee9c8b1\n--- /dev/null\n+++ b/t/t4018/elixir-function.ctx\n@@ -0,0 +1 @@\n+def function(RIGHT, arg) do\ndiff --git a/t/t4018/elixir-macro.ctx b/t/t4018/elixir-macro.ctx\nnew file mode 100644\nindex 0000000000..fc1d3b85e8\n--- /dev/null\n+++ b/t/t4018/elixir-macro.ctx\n@@ -0,0 +1 @@\n+defmacro foo(RIGHT) do\ndiff --git a/t/t4018/elixir-module-func.ctx b/t/t4018/elixir-module-func.ctx\nnew file mode 100644\nindex 0000000000..8239214386\n--- /dev/null\n+++ b/t/t4018/elixir-module-func.ctx\n@@ -0,0 +1 @@\n+def fun(RIGHT) do\ndiff --git a/t/t4018/elixir-module.ctx b/t/t4018/elixir-module.ctx\nnew file mode 100644\nindex 0000000000..8f28a7a689\n--- /dev/null\n+++ b/t/t4018/elixir-module.ctx\n@@ -0,0 +1 @@\n+defmodule RIGHT do\ndiff --git a/t/t4018/elixir-nested-module.ctx b/t/t4018/elixir-nested-module.ctx\nnew file mode 100644\nindex 0000000000..3ffbdd18b1\n--- /dev/null\n+++ b/t/t4018/elixir-nested-module.ctx\n@@ -0,0 +1 @@\n+defmodule MyApp.RIGHT do\ndiff --git a/t/t4018/elixir-private-function.ctx b/t/t4018/elixir-private-function.ctx\nnew file mode 100644\nindex 0000000000..1c4eba44f7\n--- /dev/null\n+++ b/t/t4018/elixir-private-function.ctx\n@@ -0,0 +1 @@\n+defp function(RIGHT, arg) do\ndiff --git a/t/t4018/elixir-protocol-implementation.ctx b/t/t4018/elixir-protocol-implementation.ctx\nnew file mode 100644\nindex 0000000000..efb758aea6\n--- /dev/null\n+++ b/t/t4018/elixir-protocol-implementation.ctx\n@@ -0,0 +1 @@\n+defimpl RIGHT do\ndiff --git a/t/t4018/elixir-protocol.ctx b/t/t4018/elixir-protocol.ctx\nnew file mode 100644\nindex 0000000000..d0204e9f14\n--- /dev/null\n+++ b/t/t4018/elixir-protocol.ctx\n@@ -0,0 +1 @@\n+defprotocol RIGHT do\ndiff --git a/t/t4018/fortran-block-data.ctx b/t/t4018/fortran-block-data.ctx\nnew file mode 100644\nindex 0000000000..c3db084ccc\n--- /dev/null\n+++ b/t/t4018/fortran-block-data.ctx\n@@ -0,0 +1 @@\n+BLOCK DATA RIGHT\ndiff --git a/t/t4018/fortran-comment-keyword.ctx b/t/t4018/fortran-comment-keyword.ctx\nnew file mode 100644\nindex 0000000000..0b9220b355\n--- /dev/null\n+++ b/t/t4018/fortran-comment-keyword.ctx\n@@ -0,0 +1 @@\n+subroutine RIGHT (funcA, funcB)\ndiff --git a/t/t4018/fortran-comment-legacy-star.ctx b/t/t4018/fortran-comment-legacy-star.ctx\nnew file mode 100644\nindex 0000000000..6a34203f80\n--- /dev/null\n+++ b/t/t4018/fortran-comment-legacy-star.ctx\n@@ -0,0 +1 @@\n+subroutine RIGHT\ndiff --git a/t/t4018/fortran-comment-legacy.ctx b/t/t4018/fortran-comment-legacy.ctx\nnew file mode 100644\nindex 0000000000..6a34203f80\n--- /dev/null\n+++ b/t/t4018/fortran-comment-legacy.ctx\n@@ -0,0 +1 @@\n+subroutine RIGHT\ndiff --git a/t/t4018/fortran-comment.ctx b/t/t4018/fortran-comment.ctx\nnew file mode 100644\nindex 0000000000..6a34203f80\n--- /dev/null\n+++ b/t/t4018/fortran-comment.ctx\n@@ -0,0 +1 @@\n+subroutine RIGHT\ndiff --git a/t/t4018/fortran-external-function.ctx b/t/t4018/fortran-external-function.ctx\nnew file mode 100644\nindex 0000000000..56ec4d8eca\n--- /dev/null\n+++ b/t/t4018/fortran-external-function.ctx\n@@ -0,0 +1 @@\n+function RIGHT(a, b) result(c)\ndiff --git a/t/t4018/fortran-external-subroutine.ctx b/t/t4018/fortran-external-subroutine.ctx\nnew file mode 100644\nindex 0000000000..6a34203f80\n--- /dev/null\n+++ b/t/t4018/fortran-external-subroutine.ctx\n@@ -0,0 +1 @@\n+subroutine RIGHT\ndiff --git a/t/t4018/fortran-module-procedure.ctx b/t/t4018/fortran-module-procedure.ctx\nnew file mode 100644\nindex 0000000000..4f5ff2e4b8\n--- /dev/null\n+++ b/t/t4018/fortran-module-procedure.ctx\n@@ -0,0 +1 @@\n+module RIGHT\ndiff --git a/t/t4018/fortran-module.ctx b/t/t4018/fortran-module.ctx\nnew file mode 100644\nindex 0000000000..4f5ff2e4b8\n--- /dev/null\n+++ b/t/t4018/fortran-module.ctx\n@@ -0,0 +1 @@\n+module RIGHT\ndiff --git a/t/t4018/fortran-program.ctx b/t/t4018/fortran-program.ctx\nnew file mode 100644\nindex 0000000000..c4e844df30\n--- /dev/null\n+++ b/t/t4018/fortran-program.ctx\n@@ -0,0 +1 @@\n+program RIGHT\ndiff --git a/t/t4018/fountain-scene.ctx b/t/t4018/fountain-scene.ctx\nnew file mode 100644\nindex 0000000000..bf10171418\n--- /dev/null\n+++ b/t/t4018/fountain-scene.ctx\n@@ -0,0 +1 @@\n+EXT. STREET RIGHT OUTSIDE - DAY\ndiff --git a/t/t4018/golang-complex-function.ctx b/t/t4018/golang-complex-function.ctx\nnew file mode 100644\nindex 0000000000..8e8d5582ff\n--- /dev/null\n+++ b/t/t4018/golang-complex-function.ctx\n@@ -0,0 +1 @@\n+func (t *Test) RIGHT(a Type) (Type, error) {\ndiff --git a/t/t4018/golang-func.ctx b/t/t4018/golang-func.ctx\nnew file mode 100644\nindex 0000000000..88bc823813\n--- /dev/null\n+++ b/t/t4018/golang-func.ctx\n@@ -0,0 +1 @@\n+func RIGHT() {\ndiff --git a/t/t4018/golang-interface.ctx b/t/t4018/golang-interface.ctx\nnew file mode 100644\nindex 0000000000..2d07f5a383\n--- /dev/null\n+++ b/t/t4018/golang-interface.ctx\n@@ -0,0 +1 @@\n+type RIGHT interface {\ndiff --git a/t/t4018/golang-long-func.ctx b/t/t4018/golang-long-func.ctx\nnew file mode 100644\nindex 0000000000..25635e712e\n--- /dev/null\n+++ b/t/t4018/golang-long-func.ctx\n@@ -0,0 +1 @@\n+func RIGHT(aVeryVeryVeryLongVariableName AVeryVeryVeryLongType,\ndiff --git a/t/t4018/golang-struct.ctx b/t/t4018/golang-struct.ctx\nnew file mode 100644\nindex 0000000000..8a1240699d\n--- /dev/null\n+++ b/t/t4018/golang-struct.ctx\n@@ -0,0 +1 @@\n+type RIGHT struct {\ndiff --git a/t/t4018/java-class-member-function.ctx b/t/t4018/java-class-member-function.ctx\nnew file mode 100644\nindex 0000000000..2125474b68\n--- /dev/null\n+++ b/t/t4018/java-class-member-function.ctx\n@@ -0,0 +1 @@\n+public static void main(String RIGHT[])\ndiff --git a/t/t4018/markdown-heading-indented.ctx b/t/t4018/markdown-heading-indented.ctx\nnew file mode 100644\nindex 0000000000..5938336743\n--- /dev/null\n+++ b/t/t4018/markdown-heading-indented.ctx\n@@ -0,0 +1 @@\n+   ### RIGHT\ndiff --git a/t/t4018/markdown-heading-non-headings.ctx b/t/t4018/markdown-heading-non-headings.ctx\nnew file mode 100644\nindex 0000000000..7e2165be6e\n--- /dev/null\n+++ b/t/t4018/markdown-heading-non-headings.ctx\n@@ -0,0 +1 @@\n+# RIGHT\ndiff --git a/t/t4018/matlab-class-definition.ctx b/t/t4018/matlab-class-definition.ctx\nnew file mode 100644\nindex 0000000000..5dd5b45628\n--- /dev/null\n+++ b/t/t4018/matlab-class-definition.ctx\n@@ -0,0 +1 @@\n+classdef RIGHT\ndiff --git a/t/t4018/matlab-function.ctx b/t/t4018/matlab-function.ctx\nnew file mode 100644\nindex 0000000000..72d2350b13\n--- /dev/null\n+++ b/t/t4018/matlab-function.ctx\n@@ -0,0 +1 @@\n+function y = RIGHT()\ndiff --git a/t/t4018/matlab-octave-section-1.ctx b/t/t4018/matlab-octave-section-1.ctx\nnew file mode 100644\nindex 0000000000..ca9b349f94\n--- /dev/null\n+++ b/t/t4018/matlab-octave-section-1.ctx\n@@ -0,0 +1 @@\n+%%% RIGHT section\ndiff --git a/t/t4018/matlab-octave-section-2.ctx b/t/t4018/matlab-octave-section-2.ctx\nnew file mode 100644\nindex 0000000000..5cbb77faf5\n--- /dev/null\n+++ b/t/t4018/matlab-octave-section-2.ctx\n@@ -0,0 +1 @@\n+## RIGHT section\ndiff --git a/t/t4018/matlab-section.ctx b/t/t4018/matlab-section.ctx\nnew file mode 100644\nindex 0000000000..e83fee6f4d\n--- /dev/null\n+++ b/t/t4018/matlab-section.ctx\n@@ -0,0 +1 @@\n+%% RIGHT section\ndiff --git a/t/t4018/perl-skip-end-of-heredoc.ctx b/t/t4018/perl-skip-end-of-heredoc.ctx\nnew file mode 100644\nindex 0000000000..c15f4b78bd\n--- /dev/null\n+++ b/t/t4018/perl-skip-end-of-heredoc.ctx\n@@ -0,0 +1 @@\n+sub RIGHTwithheredocument {\ndiff --git a/t/t4018/perl-skip-forward-decl.ctx b/t/t4018/perl-skip-forward-decl.ctx\nnew file mode 100644\nindex 0000000000..e0c51599ad\n--- /dev/null\n+++ b/t/t4018/perl-skip-forward-decl.ctx\n@@ -0,0 +1 @@\n+package RIGHT;\ndiff --git a/t/t4018/perl-skip-sub-in-pod.ctx b/t/t4018/perl-skip-sub-in-pod.ctx\nnew file mode 100644\nindex 0000000000..abddd76655\n--- /dev/null\n+++ b/t/t4018/perl-skip-sub-in-pod.ctx\n@@ -0,0 +1 @@\n+=head1 SYNOPSIS_RIGHT\ndiff --git a/t/t4018/perl-sub-definition-kr-brace.ctx b/t/t4018/perl-sub-definition-kr-brace.ctx\nnew file mode 100644\nindex 0000000000..7e5aee5cde\n--- /dev/null\n+++ b/t/t4018/perl-sub-definition-kr-brace.ctx\n@@ -0,0 +1 @@\n+sub RIGHT\ndiff --git a/t/t4018/perl-sub-definition.ctx b/t/t4018/perl-sub-definition.ctx\nnew file mode 100644\nindex 0000000000..d49a63598e\n--- /dev/null\n+++ b/t/t4018/perl-sub-definition.ctx\n@@ -0,0 +1 @@\n+sub RIGHT {\ndiff --git a/t/t4018/php-abstract-class.ctx b/t/t4018/php-abstract-class.ctx\nnew file mode 100644\nindex 0000000000..f572d2129b\n--- /dev/null\n+++ b/t/t4018/php-abstract-class.ctx\n@@ -0,0 +1 @@\n+abstract class RIGHT\ndiff --git a/t/t4018/php-abstract-method.ctx b/t/t4018/php-abstract-method.ctx\nnew file mode 100644\nindex 0000000000..14cb6df42e\n--- /dev/null\n+++ b/t/t4018/php-abstract-method.ctx\n@@ -0,0 +1 @@\n+abstract public function RIGHT(): ?string\ndiff --git a/t/t4018/php-class.ctx b/t/t4018/php-class.ctx\nnew file mode 100644\nindex 0000000000..54bff816d6\n--- /dev/null\n+++ b/t/t4018/php-class.ctx\n@@ -0,0 +1 @@\n+class RIGHT\ndiff --git a/t/t4018/php-final-class.ctx b/t/t4018/php-final-class.ctx\nnew file mode 100644\nindex 0000000000..4d59fb749b\n--- /dev/null\n+++ b/t/t4018/php-final-class.ctx\n@@ -0,0 +1 @@\n+final class RIGHT\ndiff --git a/t/t4018/php-final-method.ctx b/t/t4018/php-final-method.ctx\nnew file mode 100644\nindex 0000000000..b7da8f8082\n--- /dev/null\n+++ b/t/t4018/php-final-method.ctx\n@@ -0,0 +1 @@\n+final public function RIGHT(): string\ndiff --git a/t/t4018/php-function.ctx b/t/t4018/php-function.ctx\nnew file mode 100644\nindex 0000000000..c5f3e55302\n--- /dev/null\n+++ b/t/t4018/php-function.ctx\n@@ -0,0 +1 @@\n+function RIGHT()\ndiff --git a/t/t4018/php-interface.ctx b/t/t4018/php-interface.ctx\nnew file mode 100644\nindex 0000000000..a45fa0532a\n--- /dev/null\n+++ b/t/t4018/php-interface.ctx\n@@ -0,0 +1 @@\n+interface RIGHT\ndiff --git a/t/t4018/php-method.ctx b/t/t4018/php-method.ctx\nnew file mode 100644\nindex 0000000000..eb1659ff9f\n--- /dev/null\n+++ b/t/t4018/php-method.ctx\n@@ -0,0 +1 @@\n+public static function RIGHT()\ndiff --git a/t/t4018/php-trait.ctx b/t/t4018/php-trait.ctx\nnew file mode 100644\nindex 0000000000..57aa4c6267\n--- /dev/null\n+++ b/t/t4018/php-trait.ctx\n@@ -0,0 +1 @@\n+trait RIGHT\ndiff --git a/t/t4018/python-async-def.ctx b/t/t4018/python-async-def.ctx\nnew file mode 100644\nindex 0000000000..468c548bbe\n--- /dev/null\n+++ b/t/t4018/python-async-def.ctx\n@@ -0,0 +1 @@\n+async def RIGHT(pi: int = 3.14):\ndiff --git a/t/t4018/python-class.ctx b/t/t4018/python-class.ctx\nnew file mode 100644\nindex 0000000000..a40b755e29\n--- /dev/null\n+++ b/t/t4018/python-class.ctx\n@@ -0,0 +1 @@\n+class RIGHT(int, str):\ndiff --git a/t/t4018/python-def.ctx b/t/t4018/python-def.ctx\nnew file mode 100644\nindex 0000000000..a1a9cbad63\n--- /dev/null\n+++ b/t/t4018/python-def.ctx\n@@ -0,0 +1 @@\n+def RIGHT(pi: int = 3.14):\ndiff --git a/t/t4018/python-indented-async-def.ctx b/t/t4018/python-indented-async-def.ctx\nnew file mode 100644\nindex 0000000000..d393620a1e\n--- /dev/null\n+++ b/t/t4018/python-indented-async-def.ctx\n@@ -0,0 +1 @@\n+async def RIGHT(self, x: int):\ndiff --git a/t/t4018/python-indented-class.ctx b/t/t4018/python-indented-class.ctx\nnew file mode 100644\nindex 0000000000..0881c84dba\n--- /dev/null\n+++ b/t/t4018/python-indented-class.ctx\n@@ -0,0 +1 @@\n+class RIGHT:\ndiff --git a/t/t4018/python-indented-def.ctx b/t/t4018/python-indented-def.ctx\nnew file mode 100644\nindex 0000000000..6e5a44b391\n--- /dev/null\n+++ b/t/t4018/python-indented-def.ctx\n@@ -0,0 +1 @@\n+def RIGHT(self, x: int):\ndiff --git a/t/t4018/rust-fn.ctx b/t/t4018/rust-fn.ctx\nnew file mode 100644\nindex 0000000000..baa37cf253\n--- /dev/null\n+++ b/t/t4018/rust-fn.ctx\n@@ -0,0 +1 @@\n+pub(self) fn RIGHT<T>(x: &[T]) where T: Debug {\ndiff --git a/t/t4018/rust-impl.ctx b/t/t4018/rust-impl.ctx\nnew file mode 100644\nindex 0000000000..5344c35f3f\n--- /dev/null\n+++ b/t/t4018/rust-impl.ctx\n@@ -0,0 +1 @@\n+impl<'a, T: AsRef<[u8]>>  std::RIGHT for Git<'a> {\ndiff --git a/t/t4018/rust-macro-rules.ctx b/t/t4018/rust-macro-rules.ctx\nnew file mode 100644\nindex 0000000000..7520463aa0\n--- /dev/null\n+++ b/t/t4018/rust-macro-rules.ctx\n@@ -0,0 +1 @@\n+macro_rules! RIGHT {\ndiff --git a/t/t4018/rust-struct.ctx b/t/t4018/rust-struct.ctx\nnew file mode 100644\nindex 0000000000..c1e09dc808\n--- /dev/null\n+++ b/t/t4018/rust-struct.ctx\n@@ -0,0 +1 @@\n+pub(super) struct RIGHT<'a> {\ndiff --git a/t/t4018/rust-trait.ctx b/t/t4018/rust-trait.ctx\nnew file mode 100644\nindex 0000000000..6af803db29\n--- /dev/null\n+++ b/t/t4018/rust-trait.ctx\n@@ -0,0 +1 @@\n+unsafe trait RIGHT<T> {\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416926","messageId":"20210215154427.32693-9-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215005236.11313-1-avarab@gmail.com","subject":"[PATCH v2 08/27] userdiff: remove support for \"broken\" tests","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T15:44:08Z","receivedAt":"2021-02-15T15:51:21Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"There have been no \"broken\" tests since 75c3b6b2e8 (userdiff: improve\nFortran xfuncname regex, 2020-08-12). Let's remove the test support\nfor them, this is in preparation for a more general refactoring of the\ntests.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018-diff-funcname.sh | 8 +-------\n t/t4018/README           | 3 ---\n 2 files changed, 1 insertion(+), 10 deletions(-)\n\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex 6faa719536..5994c5b47a 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -82,13 +82,7 @@ test_expect_success 'setup hunk header tests' '\n # check each individual file\n for i in $(git ls-files)\n do\n-\tif grep broken \"$i\" >/dev/null 2>&1\n-\tthen\n-\t\tresult=failure\n-\telse\n-\t\tresult=success\n-\tfi\n-\ttest_expect_$result \"hunk header: $i\" \"\n+\ttest_expect_success \"hunk header: $i\" \"\n \t\tgit diff -U1 $i >actual &&\n \t\tgrep '@@ .* @@.*RIGHT' actual\n \t\"\ndiff --git a/t/t4018/README b/t/t4018/README\nindex 283e01cca1..2d25b2b4fc 100644\n--- a/t/t4018/README\n+++ b/t/t4018/README\n@@ -7,9 +7,6 @@ at least two lines from the line that must appear in the hunk header.\n The text that must appear in the hunk header must contain the word\n \"right\", but in all upper-case, like in the title above.\n \n-To mark a test case that highlights a malfunction, insert the word\n-BROKEN in all lower-case somewhere in the file.\n-\n This text is a bit twisted and out of order, but it is itself a\n test case for the default hunk header pattern. Know what you are doing\n if you change it.\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416927","messageId":"20210215154427.32693-7-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215005236.11313-1-avarab@gmail.com","subject":"[PATCH v2 06/27] userdiff tests: explicitly test \"default\" pattern","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T15:44:06Z","receivedAt":"2021-02-15T15:52:01Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Since 122aa6f9c0 (diff: introduce diff.<driver>.binary, 2008-10-05)\nthe internals of the userdiff.c code have understood a \"default\" name,\nwhich is invoked as userdiff_find_by_name(\"default\") and present in\nthe \"builtin_drivers\" struct. Let's test for this special case.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018-diff-funcname.sh | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex 9675bc17db..cefe329aea 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -49,6 +49,7 @@ diffpatterns=\"\n \truby\n \trust\n \ttex\n+\tdefault\n \tcustom1\n \tcustom2\n \tcustom3\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416928","messageId":"20210215154427.32693-15-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215005236.11313-1-avarab@gmail.com","subject":"[PATCH v2 14/27] userdiff tests: move custom patterns into one test file","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T15:44:14Z","receivedAt":"2021-02-15T15:52:01Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"In a preceding commit the test infrastructure got rewritten so\n\"t/t4018/\" are now normal test files which can do things like set\nconfig, so let's make it responsible for setting up and tearing down\nthe config for its tests.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018-diff-funcname.sh | 16 +-------\n t/t4018/custom.sh        | 79 ++++++++++++++++++++++++++++++++++++++++\n t/t4018/custom1.sh       | 27 --------------\n t/t4018/custom2.sh       | 18 ---------\n t/t4018/custom3.sh       | 27 --------------\n 5 files changed, 80 insertions(+), 87 deletions(-)\n create mode 100755 t/t4018/custom.sh\n delete mode 100755 t/t4018/custom1.sh\n delete mode 100755 t/t4018/custom2.sh\n delete mode 100755 t/t4018/custom3.sh\n\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex 4cb0b7ba2b..d80a2ad4a4 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -11,18 +11,6 @@ test_expect_success 'setup' '\n \tbuiltin_drivers=$(test-tool userdiff list-builtin-drivers) &&\n \ttest -n \"$builtin_drivers\" &&\n \n-\t# a non-trivial custom pattern\n-\tgit config diff.custom1.funcname \"!static\n-!String\n-[^ \t].*s.*\" &&\n-\n-\t# a custom pattern which matches to end of line\n-\tgit config diff.custom2.funcname \"......Beer\\$\" &&\n-\n-\t# alternation in pattern\n-\tgit config diff.custom3.funcname \"Beer$\" &&\n-\tgit config diff.custom3.xfuncname \"^[ \t]*((public|static).*)$\" &&\n-\n \t# for regexp compilation tests\n \techo A >A.java &&\n \techo B >B.java\n@@ -30,9 +18,7 @@ test_expect_success 'setup' '\n \n diffpatterns=\"\n \t$builtin_drivers\n-\tcustom1\n-\tcustom2\n-\tcustom3\n+\tcustom\n \"\n \n for p in $diffpatterns\ndiff --git a/t/t4018/custom.sh b/t/t4018/custom.sh\nnew file mode 100755\nindex 0000000000..59d855c01c\n--- /dev/null\n+++ b/t/t4018/custom.sh\n@@ -0,0 +1,79 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_expect_success 'custom: setup non-trivial custom' '\n+\tgit config diff.custom.funcname \"!static\n+!String\n+[^ \t].*s.*\"\n+'\n+\n+test_diff_funcname 'custom: non-trivial custom pattern' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+int special, RIGHT;\n+EOF_HUNK\n+public class Beer\n+{\n+\tint special, RIGHT;\n+\tpublic static void main(String args[])\n+\t{\n+\t\tString s=\" \";\n+\t\tfor(int x = 99; x > 0; x--)\n+\t\t{\n+\t\t\tSystem.out.print(x + \" bottles of beer on the wall \"\n+\t\t\t\t+ x + \" bottles of beer\\n\" // ChangeMe\n+\t\t\t\t+ \"Take one down, pass it around, \" + (x - 1)\n+\t\t\t\t+ \" bottles of beer on the wall.\\n\");\n+\t\t}\n+\t\tSystem.out.print(\"Go to the store, buy some more,\\n\"\n+\t\t\t+ \"99 bottles of beer on the wall.\\n\");\n+\t}\n+}\n+EOF_TEST\n+\n+test_expect_success 'custom: setup match to end of line' '\n+\tgit config diff.custom.funcname \"......Beer\\$\"\n+'\n+\n+test_diff_funcname 'custom: match to end of line' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT_Beer\n+EOF_HUNK\n+public class RIGHT_Beer\n+{\n+\tint special;\n+\tpublic static void main(String args[])\n+\t{\n+\t\tSystem.out.print(\"ChangeMe\");\n+\t}\n+}\n+EOF_TEST\n+\n+test_expect_success 'custom: setup alternation in pattern' '\n+\tgit config diff.custom.funcname \"Beer$\" &&\n+\tgit config diff.custom.xfuncname \"^[ \t]*((public|static).*)$\"\n+'\n+\n+test_diff_funcname 'custom: alternation in pattern' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+public static void main(String RIGHT[])\n+EOF_HUNK\n+public class Beer\n+{\n+\tint special;\n+\tpublic static void main(String RIGHT[])\n+\t{\n+\t\tString s=\" \";\n+\t\tfor(int x = 99; x > 0; x--)\n+\t\t{\n+\t\t\tSystem.out.print(x + \" bottles of beer on the wall \"\n+\t\t\t\t+ x + \" bottles of beer\\n\" // ChangeMe\n+\t\t\t\t+ \"Take one down, pass it around, \" + (x - 1)\n+\t\t\t\t+ \" bottles of beer on the wall.\\n\");\n+\t\t}\n+\t\tSystem.out.print(\"Go to the store, buy some more,\\n\"\n+\t\t\t+ \"99 bottles of beer on the wall.\\n\");\n+\t}\n+}\n+EOF_TEST\ndiff --git a/t/t4018/custom1.sh b/t/t4018/custom1.sh\ndeleted file mode 100755\nindex f8bbccadb4..0000000000\n--- a/t/t4018/custom1.sh\n+++ /dev/null\n@@ -1,27 +0,0 @@\n-#!/bin/sh\n-#\n-# See ../t4018-diff-funcname.sh's test_diff_funcname()\n-#\n-\n-test_diff_funcname 'custom1: pattern' \\\n-\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n-int special, RIGHT;\n-EOF_HUNK\n-public class Beer\n-{\n-\tint special, RIGHT;\n-\tpublic static void main(String args[])\n-\t{\n-\t\tString s=\" \";\n-\t\tfor(int x = 99; x > 0; x--)\n-\t\t{\n-\t\t\tSystem.out.print(x + \" bottles of beer on the wall \"\n-\t\t\t\t+ x + \" bottles of beer\\n\" // ChangeMe\n-\t\t\t\t+ \"Take one down, pass it around, \" + (x - 1)\n-\t\t\t\t+ \" bottles of beer on the wall.\\n\");\n-\t\t}\n-\t\tSystem.out.print(\"Go to the store, buy some more,\\n\"\n-\t\t\t+ \"99 bottles of beer on the wall.\\n\");\n-\t}\n-}\n-EOF_TEST\ndiff --git a/t/t4018/custom2.sh b/t/t4018/custom2.sh\ndeleted file mode 100755\nindex c68421f788..0000000000\n--- a/t/t4018/custom2.sh\n+++ /dev/null\n@@ -1,18 +0,0 @@\n-#!/bin/sh\n-#\n-# See ../t4018-diff-funcname.sh's test_diff_funcname()\n-#\n-\n-test_diff_funcname 'custom2: match to end of line' \\\n-\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n-RIGHT_Beer\n-EOF_HUNK\n-public class RIGHT_Beer\n-{\n-\tint special;\n-\tpublic static void main(String args[])\n-\t{\n-\t\tSystem.out.print(\"ChangeMe\");\n-\t}\n-}\n-EOF_TEST\ndiff --git a/t/t4018/custom3.sh b/t/t4018/custom3.sh\ndeleted file mode 100755\nindex 07c5c134ff..0000000000\n--- a/t/t4018/custom3.sh\n+++ /dev/null\n@@ -1,27 +0,0 @@\n-#!/bin/sh\n-#\n-# See ../t4018-diff-funcname.sh's test_diff_funcname()\n-#\n-\n-test_diff_funcname 'custom3: alternation in pattern' \\\n-\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n-public static void main(String RIGHT[])\n-EOF_HUNK\n-public class Beer\n-{\n-\tint special;\n-\tpublic static void main(String RIGHT[])\n-\t{\n-\t\tString s=\" \";\n-\t\tfor(int x = 99; x > 0; x--)\n-\t\t{\n-\t\t\tSystem.out.print(x + \" bottles of beer on the wall \"\n-\t\t\t\t+ x + \" bottles of beer\\n\" // ChangeMe\n-\t\t\t\t+ \"Take one down, pass it around, \" + (x - 1)\n-\t\t\t\t+ \" bottles of beer on the wall.\\n\");\n-\t\t}\n-\t\tSystem.out.print(\"Go to the store, buy some more,\\n\"\n-\t\t\t+ \"99 bottles of beer on the wall.\\n\");\n-\t}\n-}\n-EOF_TEST\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416929","messageId":"20210215154427.32693-26-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215005236.11313-1-avarab@gmail.com","subject":"[PATCH v2 25/27] userdiff: match \"package\" in diff=golang","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T15:44:25Z","receivedAt":"2021-02-15T15:52:21Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Improve the \"golang\" built-in pattern to match \"package\" lines, as\nthey weren't matched before changing e.g. the imports would commonly\nresult in an empty hunk header, now we'll instead show the package\nname.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018/golang.sh | 10 ++++++++++\n userdiff.c        |  2 ++\n 2 files changed, 12 insertions(+)\n\ndiff --git a/t/t4018/golang.sh b/t/t4018/golang.sh\nindex bf22f58c12..cdf9d6f8aa 100755\n--- a/t/t4018/golang.sh\n+++ b/t/t4018/golang.sh\n@@ -3,6 +3,16 @@\n # See ../t4018-diff-funcname.sh's test_diff_funcname()\n #\n \n+test_diff_funcname 'golang: package' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+package main\n+EOF_HUNK\n+package main\n+\n+import \"fmt\"\n+// ChangeMe\n+EOF_TEST\n+\n test_diff_funcname 'golang: complex function' \\\n \t8<<\\EOF_HUNK 9<<\\EOF_TEST\n func (t *Test) RIGHT(a Type) (Type, error) {\ndiff --git a/userdiff.c b/userdiff.c\nindex 55f4f769bd..f975aac8fe 100644\n--- a/userdiff.c\n+++ b/userdiff.c\n@@ -128,6 +128,8 @@ IPATTERN(\"fountain\",\n \t /* -- */\n \t \"[^ \\t-]+\"),\n PATTERNS(\"golang\",\n+\t /* Packages */\n+\t \"^[ \\t]*(package[ \\t]*(.*))\\n\"\n \t /* Functions */\n \t \"^[ \\t]*(func[ \\t]*.*(\\\\{[ \\t]*)?)\\n\"\n \t /* Structs and interfaces */\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416930","messageId":"20210215154427.32693-25-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215005236.11313-1-avarab@gmail.com","subject":"[PATCH v2 24/27] userdiff tests: assert empty hunk header context on -U<large>","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T15:44:24Z","receivedAt":"2021-02-15T15:54:27Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Assert the existing behavior that under -U<large> we'll show no hunk\nheader context, where <large> takes us past the potential hunk header\nwe'd have extracted. I'm just picking a number over nine thousand as a\nreally large number we're unlikely to exceed in these tests.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018-diff-funcname.sh | 8 ++++++++\n 1 file changed, 8 insertions(+)\n\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex d41aed9ba2..80f35c5e16 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -98,6 +98,14 @@ test_diff_funcname () {\n \t\ttest_cmp expected actual\n \t' &&\n \n+\ttest_expect_success \"$desc -U9001 (accumulated)\" '\n+\t\tgit diff -U9001 \"$what\".acc >diff &&\n+\t\tlast_diff_context_line diff >actual.lines &&\n+\t\ttail -n 1 actual.lines >actual &&\n+\t\techo >blank &&\n+\t\ttest_cmp blank actual\n+\t' &&\n+\n \ttest_expect_success \"teardown: $desc\" '\n \t\t# In case any custom config was set immediately before\n \t\t# the test itself in the test file\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416931","messageId":"20210215154427.32693-24-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215005236.11313-1-avarab@gmail.com","subject":"[PATCH v2 23/27] userdiff tests: test hunk header selection with -U0","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T15:44:23Z","receivedAt":"2021-02-15T16:02:01Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The userdiff tests have used a custom -U1 context since\nf12c66b9bb (userdiff/perl: anchor \"sub\" and \"package\" patterns on the\nleft, 2011-05-21). Changing it to -U0 doesn't change the results for\nany of the tests, except one.\n\nLet's test for this case explicitly. I.e. that we go \"beyond\" the\nselected context to find our hunk header. In many cases the desired\nhunk header is part of the diff itself under -U1.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018-diff-funcname.sh | 13 +++++++++++++\n t/t4018/custom.sh        |  1 +\n 2 files changed, 14 insertions(+)\n\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex 8b4500037f..d41aed9ba2 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -57,6 +57,7 @@ test_diff_funcname () {\n \tcat <&8 >arg.header &&\n \tcat <&9 >arg.test &&\n \twhat=$(cat arg.what) &&\n+\targ_diff_U0=$2 &&\n \n \ttest_expect_success \"setup: $desc\" '\n \t\tcp arg.test \"$what\" &&\n@@ -78,6 +79,18 @@ test_diff_funcname () {\n \t\ttest_cmp expected actual\n \t' &&\n \n+\ttest_expect_success \"$desc -U0\" '\n+\t\tgit diff -U0 \"$what\" >diff &&\n+\t\tlast_diff_context_line diff >actual &&\n+\t\tif test -n \"$arg_diff_U0\"\n+\t\tthen\n+\t\t\techo \"$arg_diff_U0\" >new-expected &&\n+\t\t\ttest_cmp new-expected actual\n+\t\telse\n+\t\t\ttest_cmp expected actual\n+\t\tfi\n+\t' &&\n+\n \ttest_expect_success \"$desc (accumulated)\" '\n \t\tgit diff -U1 \"$what\".acc >diff &&\n \t\tlast_diff_context_line diff >actual.lines &&\ndiff --git a/t/t4018/custom.sh b/t/t4018/custom.sh\nindex 886de9cddb..a090f7bfc2 100755\n--- a/t/t4018/custom.sh\n+++ b/t/t4018/custom.sh\n@@ -10,6 +10,7 @@ test_expect_success 'custom: setup non-trivial custom' '\n '\n \n test_diff_funcname 'custom: non-trivial custom pattern' \\\n+\t'System.out.print(x + \" bottles of beer on the wall \"' \\\n \t8<<\\EOF_HUNK 9<<\\EOF_TEST\n int special, RIGHT;\n EOF_HUNK\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416932","messageId":"20210215154427.32693-27-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215005236.11313-1-avarab@gmail.com","subject":"[PATCH v2 26/27] userdiff tests: add basic test for ada","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T15:44:26Z","receivedAt":"2021-02-15T16:02:30Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add test for the ada userdiff pattern added in e90d065e64 (Add\nuserdiff patterns for Ada, 2012-09-16).\n\nI don't know the ada language itself, I just stole a couple of\nexamples of code that used tokens we're matching[1][2]. Both test\nexamples stress our negative and positive matching rules.\n\n1. https://rosettacode.org/wiki/99_bottles_of_beer#Ada\n2. https://en.wikibooks.org/wiki/Ada_Programming/Tasking\n---\n t/t4018/ada.sh | 37 +++++++++++++++++++++++++++++++++++++\n 1 file changed, 37 insertions(+)\n create mode 100755 t/t4018/ada.sh\n\ndiff --git a/t/t4018/ada.sh b/t/t4018/ada.sh\nnew file mode 100755\nindex 0000000000..45fc2c7a3b\n--- /dev/null\n+++ b/t/t4018/ada.sh\n@@ -0,0 +1,37 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'ada: \"procedure\" over \"with\"' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+procedure Bottles is\n+EOF_HUNK\n+with Ada.Text_Io; use Ada.Text_Io;\n+ procedure Bottles is\n+ begin\n+    for X in reverse 1..99 loop\n+       Put_Line(Integer'Image(X) & \" bottles of beer on the wall\");\n+       Put_Line(Integer'Image(X) & \" bottles of beer\"); -- ChangeMe\n+       Put_Line(\"Take one down, pass it around\");\n+       Put_Line(Integer'Image(X - 1) & \" bottles of beer on the wall\");\n+       New_Line;\n+    end loop;\n+ end Bottles;\n+EOF_TEST\n+\n+test_diff_funcname 'ada: \"task\" over \"procedure\"' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+task body Check_CPU is\n+EOF_HUNK\n+procedure Housekeeping is\n+  task Check_CPU;\n+  task Backup_Disk;\n+\n+  task body Check_CPU is\n+    -- Comment for spacing with\n+    -- the above \"task\" for -U1\n+    ChangeMe\n+  end Check_CPU;\n+end Housekeeping;\n+EOF_TEST\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416933","messageId":"20210215154427.32693-28-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215005236.11313-1-avarab@gmail.com","subject":"[PATCH v2 27/27] userdiff tests: add basic test for ruby","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T15:44:27Z","receivedAt":"2021-02-15T16:02:30Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a test for the Ruby pattern added way back in ad8c1d9260 (diff:\nadd ruby funcname pattern, 2008-07-31).\n\nThe \"One/Two\" picking demonstrates existing behavior, and a general\ncase where we may not do what the user expects since we're not aware\nof the indentation level.\n\nThe code is modified from the Ruby code we have in-tree at\nDocumentation/asciidoctor-extensions.rb.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018/ruby.sh | 58 +++++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 58 insertions(+)\n create mode 100755 t/t4018/ruby.sh\n\ndiff --git a/t/t4018/ruby.sh b/t/t4018/ruby.sh\nnew file mode 100755\nindex 0000000000..1e9bfef863\n--- /dev/null\n+++ b/t/t4018/ruby.sh\n@@ -0,0 +1,58 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'ruby: \"def\" over \"class/module\"' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+def process(parent)\n+EOF_HUNK\n+require 'asciidoctor'\n+\n+module Git\n+  module Documentation\n+    class SomeClass\n+      use_some\n+\n+      def process(parent)\n+        puts(\"hello\")\n+\tputs(ChangeMe)\n+      end\n+    end\n+  end\n+end\n+EOF_TEST\n+\n+test_diff_funcname 'ruby: \"class\" over \"class/module\"' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+class Two\n+EOF_HUNK\n+module Git\n+  module Documentation\n+    class One\n+    end\n+\n+    class Two\n+      # Spacing for -U1\n+      ChangeMe\n+    end\n+  end\n+end\n+EOF_TEST\n+\n+test_diff_funcname 'ruby: picks first \"class/module/def\" before changed context' \\\n+\t\"class Two\" \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+class One\n+EOF_HUNK\n+module Git\n+  module Documentation\n+    class One\n+    end\n+\n+    class Two\n+      ChangeMe\n+    end\n+  end\n+end\n+EOF_TEST\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416934","messageId":"20210215154427.32693-19-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215005236.11313-1-avarab@gmail.com","subject":"[PATCH v2 18/27] gitattributes doc: reword discussion of built-in userdiff patterns","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T15:44:18Z","receivedAt":"2021-02-15T16:05:01Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Reword the discussion of the built-in userdiff patterns to make it\nmore natural to precede it with a discussion about the semantics of\npattern matching, instead of assuming that it follows right after the\n\"diff.tex.xfuncname\" example which now immediately precedes it. This\nwill make a follow-up commit smaller.\n\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/gitattributes.txt | 16 +++++++++++-----\n 1 file changed, 11 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\nindex e84e104f93..62c1147ba9 100644\n--- a/Documentation/gitattributes.txt\n+++ b/Documentation/gitattributes.txt\n@@ -794,11 +794,17 @@ backslashes; the pattern above picks a line that begins with a\n backslash, and zero or more occurrences of `sub` followed by\n `section` followed by open brace, to the end of line.\n \n-There are a few built-in patterns to make this easier, and `tex`\n-is one of them, so you do not have to write the above in your\n-configuration file (you still need to enable this with the\n-attribute mechanism, via `.gitattributes`).  The following built in\n-patterns are available:\n+There are built-in patterns shipped as part of git itself. A more\n+advanced version of the `tex` pattern discussed above is one of them.\n+\n+For built-in patterns, you do not need `diff.<lang>.xfuncname` in your\n+configuration file as discussed above, but if present, it will\n+override a built-in pattern.\n+\n+Nevertheless, you need to enable built-in patterns via .gitattributes`\n+for the pattern to take effect.\n+\n+The following built-in patterns are available:\n \n - `ada` suitable for source code in the Ada language.\n \n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416935","messageId":"20210215154427.32693-17-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215005236.11313-1-avarab@gmail.com","subject":"[PATCH v2 16/27] userdiff tests: do not do compile tests on \"custom\" pattern","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T15:44:16Z","receivedAt":"2021-02-15T16:05:03Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Since f1b75fbaf1 (t4018: convert custom pattern test to the new\ninfrastructure, 2014-03-21) we have been doing the basic sanity check\nof whether patterns in userdiff.c compile on the \"custom\" patterns.\n\nThat we were doing this was an emergent effect of that change and an\nearlier refactoring in bfa7d01413 (t4018: an infrastructure to test\nhunk headers, 2014-03-21).\n\nThis was never intended by the test added in\ne3bf5e43fd (t4018-diff-funcname: test syntax of builtin xfuncname\npatterns, 2008-09-22), nor is there any point in doing this. We'll\nerror out in the custom.sh test itself if those patterns don't\ncompile.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018-diff-funcname.sh | 9 ++-------\n 1 file changed, 2 insertions(+), 7 deletions(-)\n\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex d80a2ad4a4..3ba9d657b1 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -16,12 +16,7 @@ test_expect_success 'setup' '\n \techo B >B.java\n '\n \n-diffpatterns=\"\n-\t$builtin_drivers\n-\tcustom\n-\"\n-\n-for p in $diffpatterns\n+for p in $builtin_drivers\n do\n \ttest_expect_success \"builtin $p pattern compiles\" '\n \t\techo \"*.java diff=$p\" >.gitattributes &&\n@@ -74,7 +69,7 @@ test_diff_funcname () {\n \t'\n }\n \n-for what in $diffpatterns\n+for what in $builtin_drivers custom\n do\n \ttest=\"$TEST_DIRECTORY/t4018/$what.sh\"\n \tif ! test -e \"$test\"\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416936","messageId":"20210215154427.32693-21-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215005236.11313-1-avarab@gmail.com","subject":"[PATCH v2 20/27] userdiff tests: remove \"funcname\" from custom3 test","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T15:44:20Z","receivedAt":"2021-02-15T16:05:03Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"We can only have one \"funcname\" or \"xfuncname\", any later definition\noverrides the earlier one, so this configuration wasn't doing\nanything.\n\nWhen this test was originally added in 3632cfc248 (Use compatibility\nregex library for OSX/Darwin, 2008-09-07) we had no such definition of\ntwo patters for this test. Back then this was setting the\n\"diff.java.funcname\" configuration variable.\n\nThe stage for that second pattern being set got set later. In\n45d9414fa5 (diff.*.xfuncname which uses \"extended\" regex's for hunk\nheader selection, 2008-09-18) the pattern got converted from\n\"funcname\" to \"xfuncname\".\n\nSoon after in b19d288b4d (t4018-diff-funcname: demonstrate end of line\nfuncname matching flaw, 2008-10-15) another test immediately preceding\nthis one got added, using \"diff.java.funcname\" for its configuration.\n\nThen f792a0b88e (t4018 (funcname patterns): make configuration easier\nto track, 2011-05-21) came along and codified this whole thing when\nconverting the two tests from \"git config\" to \"test_config\".\n\nSince this was never the intent of the test let's just remove this,\nthe rationale in f792a0b88e for having some test for the clobbering\nbehavior makes sense, but I'll do that in another follow-up test, not\nas a hard to read side-effect of this one.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018/custom.sh | 1 -\n 1 file changed, 1 deletion(-)\n\ndiff --git a/t/t4018/custom.sh b/t/t4018/custom.sh\nindex 30df13d8b2..886de9cddb 100755\n--- a/t/t4018/custom.sh\n+++ b/t/t4018/custom.sh\n@@ -51,7 +51,6 @@ public class Beer\n EOF_TEST\n \n test_expect_success 'custom: setup alternation in pattern' '\n-\tgit config diff.custom.funcname \"Beer$\" &&\n \tgit config diff.custom.xfuncname \"^[ \t]*((public|static).*)$\"\n '\n \n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416938","messageId":"20210215155020.2804-1-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"[PATCH 0/2] diff: do not display hunk context under -W","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T15:50:18Z","receivedAt":"2021-02-15T16:05:04Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"This goes on top of my\nhttps://lore.kernel.org/git/20210215154427.32693-1-avarab@gmail.com/\nbecause it use its newly setup test infrastructure.\n\nÆvar Arnfjörð Bjarmason (2):\n  diff: do not display hunk context under -W\n  diff: test and document -W interaction with -U<n>\n\n Documentation/diff-options.txt | 12 ++++++++++++\n t/t4015-diff-whitespace.sh     |  2 +-\n t/t4018-diff-funcname.sh       | 12 ++++++++++++\n xdiff/xemit.c                  |  4 +++-\n 4 files changed, 28 insertions(+), 2 deletions(-)\n\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416939","messageId":"20210215155020.2804-3-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"[PATCH 2/2] diff: test and document -W interaction with -U<n>","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T15:50:20Z","receivedAt":"2021-02-15T16:05:05Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/diff-options.txt | 8 ++++++++\n t/t4018-diff-funcname.sh       | 5 +++++\n 2 files changed, 13 insertions(+)\n\ndiff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt\nindex 8ca59effa7..3c19c78616 100644\n--- a/Documentation/diff-options.txt\n+++ b/Documentation/diff-options.txt\n@@ -88,6 +88,11 @@ endif::git-log[]\n --unified=<n>::\n \tGenerate diffs with <n> lines of context instead of\n \tthe usual three.\n++\n+Under `-W` generates diffs with at least <n> lines of context, if the\n+number is lower than the context `-U<n>` would extend the diff to then\n+`-U<n>` takes precedence.\n+\n ifndef::git-format-patch[]\n \tImplies `--patch`.\n endif::git-format-patch[]\n@@ -763,6 +768,9 @@ endif::git-format-patch[]\n When showing the whole function for context the \"@@\" context line\n itself will always be empty, since the context that would otherwise be\n shown there will be the first line of the hunk being shown.\n++\n+See the documentation for `-U<n>` above for how the two options\n+interact.\n \n ifndef::git-format-patch[]\n ifndef::git-log[]\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex f3374abd98..38dc029917 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -98,6 +98,11 @@ test_diff_funcname () {\n \t\ttest_cmp W-U0-expected W-U0-actual\n \t' &&\n \n+\ttest_expect_success \"$desc -W interaction with -U<n>\" '\n+\t\tgit diff -U9001 \"$what\" >W-U9001-diff &&\n+\t\tgrep \"^@@ -1,\" W-U9001-diff\n+\t' &&\n+\n \ttest_expect_success \"$desc (accumulated)\" '\n \t\tgit diff -U1 \"$what\".acc >diff &&\n \t\tlast_diff_context_line diff >actual.lines &&\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416940","messageId":"20210215155020.2804-2-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"[PATCH 1/2] diff: do not display hunk context under -W","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T15:50:19Z","receivedAt":"2021-02-15T16:05:05Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix what I believe to be a long-standing bug in how \"-W\" interacts\nwith displaying the hunk context on the @@ line: It should not be\ndisplayed at all under -W.\n\nThe long-standing semantics of how -W works and interacts with -U<n>\nare rather easy to reason about:\n\n * -W extends the context line up to the start of the function. With\n    userdiff this means the language-aware regex rules in userdiff.c,\n    or user-supplied rules.\n\n * -U<n>, which defaults to -U3 shows at least <n> lines of context,\n    if that's greater than what we'd extend the context to under -W\n    then -U<n> wins.\n\n * When showing the hunk context we look up from the first line we\n   show of the diff, and find whatever looks like useful context above\n   that line.\n\nThus in e.g. the xdiff/xemit.c change being made in this commit we'll\ncorrectly show \"xdl_emit_diff()\" in the hunk context under default\ndiff settings.\n\nBut if we viewed it with the -W option we'd show \"is_empty_rec()\",\nbecause we'd first find the \"xdl_emit_diff()\" context line, extend the\ndiff to that, and then would go look for context to show again.\n\nI don't think this behavior makes any sense, our context in this case\nis what we're guaranteed to show as part of the diff itself.\n\nThe user already asked us to find that context line and show it, we\ndon't need to then start showing the context above that line, which\nthey didn't ask for.\n\nThis new behavior does give us the edge case that if we e.g. view the\ndiff here with \"-U150 -W\" we'd previously extend the context to the\nmiddle of the \"is_func_rec()\" function, and show that function in the\nhunk context. Now we'll show nothing.\n\nI think that change also makes sense. We're showing a change in the\n\"xdl_emit_diff()\" function. That's our context for the change. It\ndoesn't make sense with -W to start fishing around for other\ncontext.\n\nArguably in that case we could save away the context we found in the\n\"XDL_EMIT_FUNCCONTEXT\" in \"xdl_emit_diff()\" and show that if we end up\nextending the diff past the function, either because of a high -U<n>\nvalue, or because our change was right at the start.\n\nI wouldn't really mind if we did that, perhaps it would be a useful\nmarker with high -U<n> values to remind the user of what they're\nlooking at, but I also don't see the usefulness in practice, so let's\npunt that for now.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/diff-options.txt | 4 ++++\n t/t4015-diff-whitespace.sh     | 2 +-\n t/t4018-diff-funcname.sh       | 7 +++++++\n xdiff/xemit.c                  | 4 +++-\n 4 files changed, 15 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt\nindex e5733ccb2d..8ca59effa7 100644\n--- a/Documentation/diff-options.txt\n+++ b/Documentation/diff-options.txt\n@@ -759,6 +759,10 @@ endif::git-format-patch[]\n \tThe function names are determined in the same way as\n \t`git diff` works out patch hunk headers (see 'Defining a\n \tcustom hunk-header' in linkgit:gitattributes[5]).\n++\n+When showing the whole function for context the \"@@\" context line\n+itself will always be empty, since the context that would otherwise be\n+shown there will be the first line of the hunk being shown.\n \n ifndef::git-format-patch[]\n ifndef::git-log[]\ndiff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh\nindex 8c574221b2..0ffc845cdd 100755\n--- a/t/t4015-diff-whitespace.sh\n+++ b/t/t4015-diff-whitespace.sh\n@@ -2133,7 +2133,7 @@ test_expect_success 'combine --ignore-blank-lines with --function-context 2' '\n \t\t--ignore-blank-lines --function-context a b >actual.raw &&\n \tsed -n \"/@@/,\\$p\" <actual.raw >actual &&\n \tcat <<-\\EOF >expect &&\n-\t@@ -5,11 +6,9 @@ c\n+\t@@ -5,11 +6,9 @@\n \t function\n \t 1\n \t 2\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex 80f35c5e16..f3374abd98 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -91,6 +91,13 @@ test_diff_funcname () {\n \t\tfi\n \t' &&\n \n+\ttest_expect_success \"$desc -W\" '\n+\t\tgit diff -U0 -W \"$what\" >W-U0-diff &&\n+\t\techo >W-U0-expected &&\n+\t\tlast_diff_context_line W-U0-diff >W-U0-actual &&\n+\t\ttest_cmp W-U0-expected W-U0-actual\n+\t' &&\n+\n \ttest_expect_success \"$desc (accumulated)\" '\n \t\tgit diff -U1 \"$what\".acc >diff &&\n \t\tlast_diff_context_line diff >actual.lines &&\ndiff --git a/xdiff/xemit.c b/xdiff/xemit.c\nindex 9d7d6c5087..02b5dbcc70 100644\n--- a/xdiff/xemit.c\n+++ b/xdiff/xemit.c\n@@ -274,7 +274,9 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,\n \t\t */\n \n \t\tif (xecfg->flags & XDL_EMIT_FUNCNAMES) {\n-\t\t\tget_func_line(xe, xecfg, &func_line,\n+\t\t\tget_func_line(xe, xecfg,\n+\t\t\t\t      xecfg->flags & XDL_EMIT_FUNCCONTEXT\n+\t\t\t\t      ? NULL : &func_line,\n \t\t\t\t      s1 - 1, funclineprev);\n \t\t\tfunclineprev = s1 - 1;\n \t\t}\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416941","messageId":"20210215154427.32693-13-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215005236.11313-1-avarab@gmail.com","subject":"[PATCH v2 12/27] userdiff tests: rewrite hunk header test infrastructure","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T15:44:12Z","receivedAt":"2021-02-15T16:19:40Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Rewrite the hunk header test infrastructure introduced in\nbfa7d01413 (t4018: an infrastructure to test hunk headers,\n2014-03-21). See c228a5c077 (Merge branch 'js/userdiff-cc',\n2014-03-31) for the whole series that commit was part of.\n\nAs noted in an earlier commit that change introduced the regression of\nnot testing for the full hunk line, but just whether \"RIGHT\" appeared\non it[1]. A preceding commit fixed that specific issue, but we were\nstill left with the inflexibility of the approach described in the\nnow-deleted t/t4018/README.\n\nI.e. to add any sort of new tests that used the existing test data\nwe'd either need to add more files like the recently added (but now\ndeleted) *.ctx) files, using the filesystem as our test datastructure,\nor introduce more parsing for the custom file format we were growing\nhere.\n\nLet's instead just move this over to using a custom test\nfunction. This makes it trivial to add new tests by adding new\noptional parameters to the function. Let's still keep the relevant\nfiles in the \"t/t4018/\" subdirectory instead of adding ~1.5k\nlines (and growing) to \"t/t4018-diff-funcname.sh\"\n\nIf this diff is viewed with \"--color-moved=plain\" we can see that\nthere's no changes to the lines being moved into the new *.sh files,\ni.e. all the deletions are moves. I'm just adding boilerplate around\nthose existing lines.\n\nThe one-off refactoring was performed by an ad-hoc shellscript [2].\n\n1. https://lore.kernel.org/git/87wnvbbf2y.fsf@evledraar.gmail.com/\n2.\n\t#!/bin/sh\n\tset -ex\n\n\tgit rm README*\n\tfor t in $(git ls-files ':!*.ctx')\n\tdo\n\t\tlang=$(echo $t | sed 's/-.*//')\n\t\tdesc=$(echo $t | sed -E 's/^[^-]*-//' | tr - \" \")\n\n\t\tif ! test -e $lang.sh\n\t\tthen\n\t\t\tcat >$lang.sh <<-EOF\n\t\t\t#!/bin/sh\n\t\t\t#\n\t\t\t# See ../t4018-diff-funcname.sh's test_diff_funcname()\n\t\t\t#\n\n\t\t\tEOF\n\t\telse\n\t\t\techo >>$lang.sh\n\t        fi\n\n\t\t(\n\t            printf \"test_diff_funcname '%s: %s' \\\\\" \"$lang\" \"$desc\"\n\t            echo\n\t            printf \"\\t8<<%sEOF_HUNK 9<<%sEOF_TEST\\n\" '\\' '\\'\n\t            cat $t.ctx\n\t            printf \"EOF_HUNK\\n\"\n\t            cat $t\n\t            printf \"EOF_TEST\\n\"\n\t\t) >>$lang.sh\n\n\t\tchmod +x $lang.sh\n\t\tgit add $lang.sh\n\t        git rm $t $t.ctx\n\tdone\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018-diff-funcname.sh                      |  59 +++--\n t/t4018/README                                |  15 --\n t/t4018/README.ctx                            |   1 -\n t/t4018/bash-arithmetic-function              |   4 -\n t/t4018/bash-arithmetic-function.ctx          |   1 -\n t/t4018/bash-bashism-style-compact            |   6 -\n t/t4018/bash-bashism-style-compact.ctx        |   1 -\n t/t4018/bash-bashism-style-function           |   4 -\n t/t4018/bash-bashism-style-function.ctx       |   1 -\n t/t4018/bash-bashism-style-whitespace         |   4 -\n t/t4018/bash-bashism-style-whitespace.ctx     |   1 -\n t/t4018/bash-conditional-function             |   4 -\n t/t4018/bash-conditional-function.ctx         |   1 -\n t/t4018/bash-missing-parentheses              |   6 -\n t/t4018/bash-missing-parentheses.ctx          |   1 -\n t/t4018/bash-mixed-style-compact              |   4 -\n t/t4018/bash-mixed-style-compact.ctx          |   1 -\n t/t4018/bash-mixed-style-function             |   4 -\n t/t4018/bash-mixed-style-function.ctx         |   1 -\n t/t4018/bash-nested-functions                 |   6 -\n t/t4018/bash-nested-functions.ctx             |   1 -\n t/t4018/bash-other-characters                 |   4 -\n t/t4018/bash-other-characters.ctx             |   1 -\n t/t4018/bash-posix-style-compact              |   4 -\n t/t4018/bash-posix-style-compact.ctx          |   1 -\n t/t4018/bash-posix-style-function             |   4 -\n t/t4018/bash-posix-style-function.ctx         |   1 -\n t/t4018/bash-posix-style-whitespace           |   4 -\n t/t4018/bash-posix-style-whitespace.ctx       |   1 -\n t/t4018/bash-subshell-function                |   4 -\n t/t4018/bash-subshell-function.ctx            |   1 -\n t/t4018/bash-trailing-comment                 |   4 -\n t/t4018/bash-trailing-comment.ctx             |   1 -\n t/t4018/bash.sh                               | 160 ++++++++++++\n t/t4018/cpp-c++-function                      |   4 -\n t/t4018/cpp-c++-function.ctx                  |   1 -\n t/t4018/cpp-class-constructor                 |   4 -\n t/t4018/cpp-class-constructor-mem-init        |   5 -\n t/t4018/cpp-class-constructor-mem-init.ctx    |   1 -\n t/t4018/cpp-class-constructor.ctx             |   1 -\n t/t4018/cpp-class-definition                  |   4 -\n t/t4018/cpp-class-definition-derived          |   5 -\n t/t4018/cpp-class-definition-derived.ctx      |   1 -\n t/t4018/cpp-class-definition.ctx              |   1 -\n t/t4018/cpp-class-destructor                  |   4 -\n t/t4018/cpp-class-destructor.ctx              |   1 -\n t/t4018/cpp-function-returning-global-type    |   4 -\n .../cpp-function-returning-global-type.ctx    |   1 -\n t/t4018/cpp-function-returning-nested         |   5 -\n t/t4018/cpp-function-returning-nested.ctx     |   1 -\n t/t4018/cpp-function-returning-pointer        |   4 -\n t/t4018/cpp-function-returning-pointer.ctx    |   1 -\n t/t4018/cpp-function-returning-reference      |   4 -\n t/t4018/cpp-function-returning-reference.ctx  |   1 -\n t/t4018/cpp-gnu-style-function                |   5 -\n t/t4018/cpp-gnu-style-function.ctx            |   1 -\n t/t4018/cpp-namespace-definition              |   4 -\n t/t4018/cpp-namespace-definition.ctx          |   1 -\n t/t4018/cpp-operator-definition               |   4 -\n t/t4018/cpp-operator-definition.ctx           |   1 -\n t/t4018/cpp-skip-access-specifiers            |   8 -\n t/t4018/cpp-skip-access-specifiers.ctx        |   1 -\n t/t4018/cpp-skip-comment-block                |   9 -\n t/t4018/cpp-skip-comment-block.ctx            |   1 -\n t/t4018/cpp-skip-labels                       |   8 -\n t/t4018/cpp-skip-labels.ctx                   |   1 -\n t/t4018/cpp-struct-definition                 |   9 -\n t/t4018/cpp-struct-definition.ctx             |   1 -\n t/t4018/cpp-struct-single-line                |   7 -\n t/t4018/cpp-struct-single-line.ctx            |   1 -\n t/t4018/cpp-template-function-definition      |   4 -\n t/t4018/cpp-template-function-definition.ctx  |   1 -\n t/t4018/cpp-union-definition                  |   4 -\n t/t4018/cpp-union-definition.ctx              |   1 -\n t/t4018/cpp-void-c-function                   |   4 -\n t/t4018/cpp-void-c-function.ctx               |   1 -\n t/t4018/cpp.sh                                | 239 ++++++++++++++++++\n t/t4018/css-attribute-value-selector          |   4 -\n t/t4018/css-attribute-value-selector.ctx      |   1 -\n t/t4018/css-block-level-@-statements          |  10 -\n t/t4018/css-block-level-@-statements.ctx      |   1 -\n t/t4018/css-brace-in-col-1                    |   5 -\n t/t4018/css-brace-in-col-1.ctx                |   1 -\n t/t4018/css-class-selector                    |   4 -\n t/t4018/css-class-selector.ctx                |   1 -\n t/t4018/css-colon-eol                         |   4 -\n t/t4018/css-colon-eol.ctx                     |   1 -\n t/t4018/css-colon-selector                    |   5 -\n t/t4018/css-colon-selector.ctx                |   1 -\n t/t4018/css-common                            |   4 -\n t/t4018/css-common.ctx                        |   1 -\n t/t4018/css-id-selector                       |   4 -\n t/t4018/css-id-selector.ctx                   |   1 -\n t/t4018/css-long-selector-list                |   6 -\n t/t4018/css-long-selector-list.ctx            |   1 -\n t/t4018/css-prop-sans-indent                  |   5 -\n t/t4018/css-prop-sans-indent.ctx              |   1 -\n t/t4018/css-root-selector                     |   4 -\n t/t4018/css-root-selector.ctx                 |   1 -\n t/t4018/css-short-selector-list               |   4 -\n t/t4018/css-short-selector-list.ctx           |   1 -\n t/t4018/css-trailing-space                    |   5 -\n t/t4018/css-trailing-space.ctx                |   1 -\n t/t4018/css.sh                                | 146 +++++++++++\n t/t4018/custom1-pattern.ctx                   |   1 -\n t/t4018/{custom1-pattern => custom1.sh}       |  10 +\n t/t4018/custom2-match-to-end-of-line          |   8 -\n t/t4018/custom2-match-to-end-of-line.ctx      |   1 -\n t/t4018/custom2.sh                            |  18 ++\n t/t4018/custom3-alternation-in-pattern.ctx    |   1 -\n ...tom3-alternation-in-pattern => custom3.sh} |  10 +\n t/t4018/dts-labels                            |   9 -\n t/t4018/dts-labels.ctx                        |   1 -\n t/t4018/dts-node-unitless                     |   8 -\n t/t4018/dts-node-unitless.ctx                 |   1 -\n t/t4018/dts-nodes                             |   8 -\n t/t4018/dts-nodes-boolean-prop                |   9 -\n t/t4018/dts-nodes-boolean-prop.ctx            |   1 -\n t/t4018/dts-nodes-comment1                    |   8 -\n t/t4018/dts-nodes-comment1.ctx                |   1 -\n t/t4018/dts-nodes-comment2                    |   8 -\n t/t4018/dts-nodes-comment2.ctx                |   1 -\n t/t4018/dts-nodes-multiline-prop              |  13 -\n t/t4018/dts-nodes-multiline-prop.ctx          |   1 -\n t/t4018/dts-nodes.ctx                         |   1 -\n t/t4018/dts-reference                         |   9 -\n t/t4018/dts-reference.ctx                     |   1 -\n t/t4018/dts-root                              |   5 -\n t/t4018/dts-root-comment                      |   8 -\n t/t4018/dts-root-comment.ctx                  |   1 -\n t/t4018/dts-root.ctx                          |   1 -\n t/t4018/dts.sh                                | 149 +++++++++++\n t/t4018/elixir-do-not-pick-end                |   5 -\n t/t4018/elixir-do-not-pick-end.ctx            |   1 -\n t/t4018/elixir-ex-unit-test                   |   6 -\n t/t4018/elixir-ex-unit-test.ctx               |   1 -\n t/t4018/elixir-function                       |   5 -\n t/t4018/elixir-function.ctx                   |   1 -\n t/t4018/elixir-macro                          |   5 -\n t/t4018/elixir-macro.ctx                      |   1 -\n t/t4018/elixir-module                         |   9 -\n t/t4018/elixir-module-func                    |   8 -\n t/t4018/elixir-module-func.ctx                |   1 -\n t/t4018/elixir-module.ctx                     |   1 -\n t/t4018/elixir-nested-module                  |   9 -\n t/t4018/elixir-nested-module.ctx              |   1 -\n t/t4018/elixir-private-function               |   5 -\n t/t4018/elixir-private-function.ctx           |   1 -\n t/t4018/elixir-protocol                       |   6 -\n t/t4018/elixir-protocol-implementation        |   5 -\n t/t4018/elixir-protocol-implementation.ctx    |   1 -\n t/t4018/elixir-protocol.ctx                   |   1 -\n t/t4018/elixir.sh                             | 127 ++++++++++\n t/t4018/fortran-block-data                    |   5 -\n t/t4018/fortran-block-data.ctx                |   1 -\n t/t4018/fortran-comment                       |  13 -\n t/t4018/fortran-comment-keyword               |  14 -\n t/t4018/fortran-comment-keyword.ctx           |   1 -\n t/t4018/fortran-comment-legacy                |  13 -\n t/t4018/fortran-comment-legacy-star           |  13 -\n t/t4018/fortran-comment-legacy-star.ctx       |   1 -\n t/t4018/fortran-comment-legacy.ctx            |   1 -\n t/t4018/fortran-comment.ctx                   |   1 -\n t/t4018/fortran-external-function             |   9 -\n t/t4018/fortran-external-function.ctx         |   1 -\n t/t4018/fortran-external-subroutine           |   5 -\n t/t4018/fortran-external-subroutine.ctx       |   1 -\n t/t4018/fortran-module                        |   5 -\n t/t4018/fortran-module-procedure              |  13 -\n t/t4018/fortran-module-procedure.ctx          |   1 -\n t/t4018/fortran-module.ctx                    |   1 -\n t/t4018/fortran-program                       |   5 -\n t/t4018/fortran-program.ctx                   |   1 -\n t/t4018/fortran.sh                            | 159 ++++++++++++\n t/t4018/fountain-scene                        |   4 -\n t/t4018/fountain-scene.ctx                    |   1 -\n t/t4018/fountain.sh                           |  14 +\n t/t4018/golang-complex-function               |   8 -\n t/t4018/golang-complex-function.ctx           |   1 -\n t/t4018/golang-func                           |   4 -\n t/t4018/golang-func.ctx                       |   1 -\n t/t4018/golang-interface                      |   4 -\n t/t4018/golang-interface.ctx                  |   1 -\n t/t4018/golang-long-func                      |   5 -\n t/t4018/golang-long-func.ctx                  |   1 -\n t/t4018/golang-struct                         |   4 -\n t/t4018/golang-struct.ctx                     |   1 -\n t/t4018/golang.sh                             |  59 +++++\n t/t4018/java-class-member-function            |   8 -\n t/t4018/java-class-member-function.ctx        |   1 -\n t/t4018/java.sh                               |  18 ++\n t/t4018/markdown-heading-indented             |   6 -\n t/t4018/markdown-heading-indented.ctx         |   1 -\n t/t4018/markdown-heading-non-headings         |  17 --\n t/t4018/markdown-heading-non-headings.ctx     |   1 -\n t/t4018/markdown.sh                           |  39 +++\n t/t4018/matlab-class-definition               |   5 -\n t/t4018/matlab-class-definition.ctx           |   1 -\n t/t4018/matlab-function                       |   4 -\n t/t4018/matlab-function.ctx                   |   1 -\n t/t4018/matlab-octave-section-1               |   3 -\n t/t4018/matlab-octave-section-1.ctx           |   1 -\n t/t4018/matlab-octave-section-2               |   3 -\n t/t4018/matlab-octave-section-2.ctx           |   1 -\n t/t4018/matlab-section                        |   3 -\n t/t4018/matlab-section.ctx                    |   1 -\n t/t4018/matlab.sh                             |  52 ++++\n t/t4018/perl-skip-end-of-heredoc              |   8 -\n t/t4018/perl-skip-end-of-heredoc.ctx          |   1 -\n t/t4018/perl-skip-forward-decl                |  10 -\n t/t4018/perl-skip-forward-decl.ctx            |   1 -\n t/t4018/perl-skip-sub-in-pod                  |  18 --\n t/t4018/perl-skip-sub-in-pod.ctx              |   1 -\n t/t4018/perl-sub-definition                   |   4 -\n t/t4018/perl-sub-definition-kr-brace          |   4 -\n t/t4018/perl-sub-definition-kr-brace.ctx      |   1 -\n t/t4018/perl-sub-definition.ctx               |   1 -\n t/t4018/perl.sh                               |  78 ++++++\n t/t4018/php-abstract-class                    |   4 -\n t/t4018/php-abstract-class.ctx                |   1 -\n t/t4018/php-abstract-method                   |   7 -\n t/t4018/php-abstract-method.ctx               |   1 -\n t/t4018/php-class                             |   4 -\n t/t4018/php-class.ctx                         |   1 -\n t/t4018/php-final-class                       |   4 -\n t/t4018/php-final-class.ctx                   |   1 -\n t/t4018/php-final-method                      |   7 -\n t/t4018/php-final-method.ctx                  |   1 -\n t/t4018/php-function                          |   4 -\n t/t4018/php-function.ctx                      |   1 -\n t/t4018/php-interface                         |   4 -\n t/t4018/php-interface.ctx                     |   1 -\n t/t4018/php-method                            |   7 -\n t/t4018/php-method.ctx                        |   1 -\n t/t4018/php-trait                             |   7 -\n t/t4018/php-trait.ctx                         |   1 -\n t/t4018/php.sh                                | 106 ++++++++\n t/t4018/python-async-def                      |   4 -\n t/t4018/python-async-def.ctx                  |   1 -\n t/t4018/python-class                          |   4 -\n t/t4018/python-class.ctx                      |   1 -\n t/t4018/python-def                            |   4 -\n t/t4018/python-def.ctx                        |   1 -\n t/t4018/python-indented-async-def             |   7 -\n t/t4018/python-indented-async-def.ctx         |   1 -\n t/t4018/python-indented-class                 |   5 -\n t/t4018/python-indented-class.ctx             |   1 -\n t/t4018/python-indented-def                   |   7 -\n t/t4018/python-indented-def.ctx               |   1 -\n t/t4018/python.sh                             |  71 ++++++\n t/t4018/rust-fn                               |   5 -\n t/t4018/rust-fn.ctx                           |   1 -\n t/t4018/rust-impl                             |   5 -\n t/t4018/rust-impl.ctx                         |   1 -\n t/t4018/rust-macro-rules                      |   6 -\n t/t4018/rust-macro-rules.ctx                  |   1 -\n t/t4018/rust-struct                           |   5 -\n t/t4018/rust-struct.ctx                       |   1 -\n t/t4018/rust-trait                            |   5 -\n t/t4018/rust-trait.ctx                        |   1 -\n t/t4018/rust.sh                               |  60 +++++\n 261 files changed, 1549 insertions(+), 879 deletions(-)\n delete mode 100644 t/t4018/README\n delete mode 100644 t/t4018/README.ctx\n delete mode 100644 t/t4018/bash-arithmetic-function\n delete mode 100644 t/t4018/bash-arithmetic-function.ctx\n delete mode 100644 t/t4018/bash-bashism-style-compact\n delete mode 100644 t/t4018/bash-bashism-style-compact.ctx\n delete mode 100644 t/t4018/bash-bashism-style-function\n delete mode 100644 t/t4018/bash-bashism-style-function.ctx\n delete mode 100644 t/t4018/bash-bashism-style-whitespace\n delete mode 100644 t/t4018/bash-bashism-style-whitespace.ctx\n delete mode 100644 t/t4018/bash-conditional-function\n delete mode 100644 t/t4018/bash-conditional-function.ctx\n delete mode 100644 t/t4018/bash-missing-parentheses\n delete mode 100644 t/t4018/bash-missing-parentheses.ctx\n delete mode 100644 t/t4018/bash-mixed-style-compact\n delete mode 100644 t/t4018/bash-mixed-style-compact.ctx\n delete mode 100644 t/t4018/bash-mixed-style-function\n delete mode 100644 t/t4018/bash-mixed-style-function.ctx\n delete mode 100644 t/t4018/bash-nested-functions\n delete mode 100644 t/t4018/bash-nested-functions.ctx\n delete mode 100644 t/t4018/bash-other-characters\n delete mode 100644 t/t4018/bash-other-characters.ctx\n delete mode 100644 t/t4018/bash-posix-style-compact\n delete mode 100644 t/t4018/bash-posix-style-compact.ctx\n delete mode 100644 t/t4018/bash-posix-style-function\n delete mode 100644 t/t4018/bash-posix-style-function.ctx\n delete mode 100644 t/t4018/bash-posix-style-whitespace\n delete mode 100644 t/t4018/bash-posix-style-whitespace.ctx\n delete mode 100644 t/t4018/bash-subshell-function\n delete mode 100644 t/t4018/bash-subshell-function.ctx\n delete mode 100644 t/t4018/bash-trailing-comment\n delete mode 100644 t/t4018/bash-trailing-comment.ctx\n create mode 100755 t/t4018/bash.sh\n delete mode 100644 t/t4018/cpp-c++-function\n delete mode 100644 t/t4018/cpp-c++-function.ctx\n delete mode 100644 t/t4018/cpp-class-constructor\n delete mode 100644 t/t4018/cpp-class-constructor-mem-init\n delete mode 100644 t/t4018/cpp-class-constructor-mem-init.ctx\n delete mode 100644 t/t4018/cpp-class-constructor.ctx\n delete mode 100644 t/t4018/cpp-class-definition\n delete mode 100644 t/t4018/cpp-class-definition-derived\n delete mode 100644 t/t4018/cpp-class-definition-derived.ctx\n delete mode 100644 t/t4018/cpp-class-definition.ctx\n delete mode 100644 t/t4018/cpp-class-destructor\n delete mode 100644 t/t4018/cpp-class-destructor.ctx\n delete mode 100644 t/t4018/cpp-function-returning-global-type\n delete mode 100644 t/t4018/cpp-function-returning-global-type.ctx\n delete mode 100644 t/t4018/cpp-function-returning-nested\n delete mode 100644 t/t4018/cpp-function-returning-nested.ctx\n delete mode 100644 t/t4018/cpp-function-returning-pointer\n delete mode 100644 t/t4018/cpp-function-returning-pointer.ctx\n delete mode 100644 t/t4018/cpp-function-returning-reference\n delete mode 100644 t/t4018/cpp-function-returning-reference.ctx\n delete mode 100644 t/t4018/cpp-gnu-style-function\n delete mode 100644 t/t4018/cpp-gnu-style-function.ctx\n delete mode 100644 t/t4018/cpp-namespace-definition\n delete mode 100644 t/t4018/cpp-namespace-definition.ctx\n delete mode 100644 t/t4018/cpp-operator-definition\n delete mode 100644 t/t4018/cpp-operator-definition.ctx\n delete mode 100644 t/t4018/cpp-skip-access-specifiers\n delete mode 100644 t/t4018/cpp-skip-access-specifiers.ctx\n delete mode 100644 t/t4018/cpp-skip-comment-block\n delete mode 100644 t/t4018/cpp-skip-comment-block.ctx\n delete mode 100644 t/t4018/cpp-skip-labels\n delete mode 100644 t/t4018/cpp-skip-labels.ctx\n delete mode 100644 t/t4018/cpp-struct-definition\n delete mode 100644 t/t4018/cpp-struct-definition.ctx\n delete mode 100644 t/t4018/cpp-struct-single-line\n delete mode 100644 t/t4018/cpp-struct-single-line.ctx\n delete mode 100644 t/t4018/cpp-template-function-definition\n delete mode 100644 t/t4018/cpp-template-function-definition.ctx\n delete mode 100644 t/t4018/cpp-union-definition\n delete mode 100644 t/t4018/cpp-union-definition.ctx\n delete mode 100644 t/t4018/cpp-void-c-function\n delete mode 100644 t/t4018/cpp-void-c-function.ctx\n create mode 100755 t/t4018/cpp.sh\n delete mode 100644 t/t4018/css-attribute-value-selector\n delete mode 100644 t/t4018/css-attribute-value-selector.ctx\n delete mode 100644 t/t4018/css-block-level-@-statements\n delete mode 100644 t/t4018/css-block-level-@-statements.ctx\n delete mode 100644 t/t4018/css-brace-in-col-1\n delete mode 100644 t/t4018/css-brace-in-col-1.ctx\n delete mode 100644 t/t4018/css-class-selector\n delete mode 100644 t/t4018/css-class-selector.ctx\n delete mode 100644 t/t4018/css-colon-eol\n delete mode 100644 t/t4018/css-colon-eol.ctx\n delete mode 100644 t/t4018/css-colon-selector\n delete mode 100644 t/t4018/css-colon-selector.ctx\n delete mode 100644 t/t4018/css-common\n delete mode 100644 t/t4018/css-common.ctx\n delete mode 100644 t/t4018/css-id-selector\n delete mode 100644 t/t4018/css-id-selector.ctx\n delete mode 100644 t/t4018/css-long-selector-list\n delete mode 100644 t/t4018/css-long-selector-list.ctx\n delete mode 100644 t/t4018/css-prop-sans-indent\n delete mode 100644 t/t4018/css-prop-sans-indent.ctx\n delete mode 100644 t/t4018/css-root-selector\n delete mode 100644 t/t4018/css-root-selector.ctx\n delete mode 100644 t/t4018/css-short-selector-list\n delete mode 100644 t/t4018/css-short-selector-list.ctx\n delete mode 100644 t/t4018/css-trailing-space\n delete mode 100644 t/t4018/css-trailing-space.ctx\n create mode 100755 t/t4018/css.sh\n delete mode 100644 t/t4018/custom1-pattern.ctx\n rename t/t4018/{custom1-pattern => custom1.sh} (71%)\n mode change 100644 => 100755\n delete mode 100644 t/t4018/custom2-match-to-end-of-line\n delete mode 100644 t/t4018/custom2-match-to-end-of-line.ctx\n create mode 100755 t/t4018/custom2.sh\n delete mode 100644 t/t4018/custom3-alternation-in-pattern.ctx\n rename t/t4018/{custom3-alternation-in-pattern => custom3.sh} (66%)\n mode change 100644 => 100755\n delete mode 100644 t/t4018/dts-labels\n delete mode 100644 t/t4018/dts-labels.ctx\n delete mode 100644 t/t4018/dts-node-unitless\n delete mode 100644 t/t4018/dts-node-unitless.ctx\n delete mode 100644 t/t4018/dts-nodes\n delete mode 100644 t/t4018/dts-nodes-boolean-prop\n delete mode 100644 t/t4018/dts-nodes-boolean-prop.ctx\n delete mode 100644 t/t4018/dts-nodes-comment1\n delete mode 100644 t/t4018/dts-nodes-comment1.ctx\n delete mode 100644 t/t4018/dts-nodes-comment2\n delete mode 100644 t/t4018/dts-nodes-comment2.ctx\n delete mode 100644 t/t4018/dts-nodes-multiline-prop\n delete mode 100644 t/t4018/dts-nodes-multiline-prop.ctx\n delete mode 100644 t/t4018/dts-nodes.ctx\n delete mode 100644 t/t4018/dts-reference\n delete mode 100644 t/t4018/dts-reference.ctx\n delete mode 100644 t/t4018/dts-root\n delete mode 100644 t/t4018/dts-root-comment\n delete mode 100644 t/t4018/dts-root-comment.ctx\n delete mode 100644 t/t4018/dts-root.ctx\n create mode 100755 t/t4018/dts.sh\n delete mode 100644 t/t4018/elixir-do-not-pick-end\n delete mode 100644 t/t4018/elixir-do-not-pick-end.ctx\n delete mode 100644 t/t4018/elixir-ex-unit-test\n delete mode 100644 t/t4018/elixir-ex-unit-test.ctx\n delete mode 100644 t/t4018/elixir-function\n delete mode 100644 t/t4018/elixir-function.ctx\n delete mode 100644 t/t4018/elixir-macro\n delete mode 100644 t/t4018/elixir-macro.ctx\n delete mode 100644 t/t4018/elixir-module\n delete mode 100644 t/t4018/elixir-module-func\n delete mode 100644 t/t4018/elixir-module-func.ctx\n delete mode 100644 t/t4018/elixir-module.ctx\n delete mode 100644 t/t4018/elixir-nested-module\n delete mode 100644 t/t4018/elixir-nested-module.ctx\n delete mode 100644 t/t4018/elixir-private-function\n delete mode 100644 t/t4018/elixir-private-function.ctx\n delete mode 100644 t/t4018/elixir-protocol\n delete mode 100644 t/t4018/elixir-protocol-implementation\n delete mode 100644 t/t4018/elixir-protocol-implementation.ctx\n delete mode 100644 t/t4018/elixir-protocol.ctx\n create mode 100755 t/t4018/elixir.sh\n delete mode 100644 t/t4018/fortran-block-data\n delete mode 100644 t/t4018/fortran-block-data.ctx\n delete mode 100644 t/t4018/fortran-comment\n delete mode 100644 t/t4018/fortran-comment-keyword\n delete mode 100644 t/t4018/fortran-comment-keyword.ctx\n delete mode 100644 t/t4018/fortran-comment-legacy\n delete mode 100644 t/t4018/fortran-comment-legacy-star\n delete mode 100644 t/t4018/fortran-comment-legacy-star.ctx\n delete mode 100644 t/t4018/fortran-comment-legacy.ctx\n delete mode 100644 t/t4018/fortran-comment.ctx\n delete mode 100644 t/t4018/fortran-external-function\n delete mode 100644 t/t4018/fortran-external-function.ctx\n delete mode 100644 t/t4018/fortran-external-subroutine\n delete mode 100644 t/t4018/fortran-external-subroutine.ctx\n delete mode 100644 t/t4018/fortran-module\n delete mode 100644 t/t4018/fortran-module-procedure\n delete mode 100644 t/t4018/fortran-module-procedure.ctx\n delete mode 100644 t/t4018/fortran-module.ctx\n delete mode 100644 t/t4018/fortran-program\n delete mode 100644 t/t4018/fortran-program.ctx\n create mode 100755 t/t4018/fortran.sh\n delete mode 100644 t/t4018/fountain-scene\n delete mode 100644 t/t4018/fountain-scene.ctx\n create mode 100755 t/t4018/fountain.sh\n delete mode 100644 t/t4018/golang-complex-function\n delete mode 100644 t/t4018/golang-complex-function.ctx\n delete mode 100644 t/t4018/golang-func\n delete mode 100644 t/t4018/golang-func.ctx\n delete mode 100644 t/t4018/golang-interface\n delete mode 100644 t/t4018/golang-interface.ctx\n delete mode 100644 t/t4018/golang-long-func\n delete mode 100644 t/t4018/golang-long-func.ctx\n delete mode 100644 t/t4018/golang-struct\n delete mode 100644 t/t4018/golang-struct.ctx\n create mode 100755 t/t4018/golang.sh\n delete mode 100644 t/t4018/java-class-member-function\n delete mode 100644 t/t4018/java-class-member-function.ctx\n create mode 100755 t/t4018/java.sh\n delete mode 100644 t/t4018/markdown-heading-indented\n delete mode 100644 t/t4018/markdown-heading-indented.ctx\n delete mode 100644 t/t4018/markdown-heading-non-headings\n delete mode 100644 t/t4018/markdown-heading-non-headings.ctx\n create mode 100755 t/t4018/markdown.sh\n delete mode 100644 t/t4018/matlab-class-definition\n delete mode 100644 t/t4018/matlab-class-definition.ctx\n delete mode 100644 t/t4018/matlab-function\n delete mode 100644 t/t4018/matlab-function.ctx\n delete mode 100644 t/t4018/matlab-octave-section-1\n delete mode 100644 t/t4018/matlab-octave-section-1.ctx\n delete mode 100644 t/t4018/matlab-octave-section-2\n delete mode 100644 t/t4018/matlab-octave-section-2.ctx\n delete mode 100644 t/t4018/matlab-section\n delete mode 100644 t/t4018/matlab-section.ctx\n create mode 100755 t/t4018/matlab.sh\n delete mode 100644 t/t4018/perl-skip-end-of-heredoc\n delete mode 100644 t/t4018/perl-skip-end-of-heredoc.ctx\n delete mode 100644 t/t4018/perl-skip-forward-decl\n delete mode 100644 t/t4018/perl-skip-forward-decl.ctx\n delete mode 100644 t/t4018/perl-skip-sub-in-pod\n delete mode 100644 t/t4018/perl-skip-sub-in-pod.ctx\n delete mode 100644 t/t4018/perl-sub-definition\n delete mode 100644 t/t4018/perl-sub-definition-kr-brace\n delete mode 100644 t/t4018/perl-sub-definition-kr-brace.ctx\n delete mode 100644 t/t4018/perl-sub-definition.ctx\n create mode 100755 t/t4018/perl.sh\n delete mode 100644 t/t4018/php-abstract-class\n delete mode 100644 t/t4018/php-abstract-class.ctx\n delete mode 100644 t/t4018/php-abstract-method\n delete mode 100644 t/t4018/php-abstract-method.ctx\n delete mode 100644 t/t4018/php-class\n delete mode 100644 t/t4018/php-class.ctx\n delete mode 100644 t/t4018/php-final-class\n delete mode 100644 t/t4018/php-final-class.ctx\n delete mode 100644 t/t4018/php-final-method\n delete mode 100644 t/t4018/php-final-method.ctx\n delete mode 100644 t/t4018/php-function\n delete mode 100644 t/t4018/php-function.ctx\n delete mode 100644 t/t4018/php-interface\n delete mode 100644 t/t4018/php-interface.ctx\n delete mode 100644 t/t4018/php-method\n delete mode 100644 t/t4018/php-method.ctx\n delete mode 100644 t/t4018/php-trait\n delete mode 100644 t/t4018/php-trait.ctx\n create mode 100755 t/t4018/php.sh\n delete mode 100644 t/t4018/python-async-def\n delete mode 100644 t/t4018/python-async-def.ctx\n delete mode 100644 t/t4018/python-class\n delete mode 100644 t/t4018/python-class.ctx\n delete mode 100644 t/t4018/python-def\n delete mode 100644 t/t4018/python-def.ctx\n delete mode 100644 t/t4018/python-indented-async-def\n delete mode 100644 t/t4018/python-indented-async-def.ctx\n delete mode 100644 t/t4018/python-indented-class\n delete mode 100644 t/t4018/python-indented-class.ctx\n delete mode 100644 t/t4018/python-indented-def\n delete mode 100644 t/t4018/python-indented-def.ctx\n create mode 100755 t/t4018/python.sh\n delete mode 100644 t/t4018/rust-fn\n delete mode 100644 t/t4018/rust-fn.ctx\n delete mode 100644 t/t4018/rust-impl\n delete mode 100644 t/t4018/rust-impl.ctx\n delete mode 100644 t/t4018/rust-macro-rules\n delete mode 100644 t/t4018/rust-macro-rules.ctx\n delete mode 100644 t/t4018/rust-struct\n delete mode 100644 t/t4018/rust-struct.ctx\n delete mode 100644 t/t4018/rust-trait\n delete mode 100644 t/t4018/rust-trait.ctx\n create mode 100755 t/t4018/rust.sh\n\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex 3941316682..decf7961f9 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -60,33 +60,42 @@ test_expect_success 'last regexp must not be negated' '\n \ttest_i18ngrep \": Last expression must not be negated:\" msg\n '\n \n-test_expect_success 'setup hunk header tests' '\n-\tfor i in $diffpatterns\n-\tdo\n-\t\techo \"$i-* diff=$i\"\n-\tdone > .gitattributes &&\n-\n-\t# add all test files to the index\n-\t(\n-\t\tcd \"$TEST_DIRECTORY\"/t4018 &&\n-\t\tgit --git-dir=\"$TRASH_DIRECTORY/.git\" add .\n-\t) &&\n-\n-\t# place modified files in the worktree\n-\tfor i in $(git ls-files)\n-\tdo\n-\t\tsed -e \"s/ChangeMe/IWasChanged/\" <\"$TEST_DIRECTORY/t4018/$i\" >\"$i\" || return 1\n-\tdone\n-'\n+test_diff_funcname () {\n+\tdesc=$1\n+\tcat <&8 >arg.header &&\n+\tcat <&9 >arg.test &&\n+\twhat=$(cat arg.what) &&\n+\n+\ttest_expect_success \"setup: $desc\" '\n+\t\tcp arg.test \"$what\" &&\n+\t\tcp arg.header expected &&\n+\t\tgit add \"$what\" &&\n+\t\tsed -e \"s/ChangeMe/IWasChanged/\" <\"$what\" >tmp &&\n+\t\tmv tmp \"$what\"\n+\t' &&\n+\n+\ttest_expect_success \"$desc\" '\n+\t\tgit diff -U1 \"$what\" >diff &&\n+\t\tsed -n -e \"s/^.*@@$//p\" -e \"s/^.*@@ //p\" <diff >actual &&\n+\t\ttest_cmp expected actual\n+\t'\n+}\n \n-# check each individual file\n-for i in $(git ls-files -- ':!*.ctx')\n+for what in $diffpatterns\n do\n-\ttest_expect_success \"hunk header: $i\" \"\n-\t\tgit diff -U1 $i >diff &&\n-\t\tsed -n -e 's/^.*@@$//p' -e 's/^.*@@ //p' <diff >ctx &&\n-\t\ttest_cmp $i.ctx ctx\n-\t\"\n+\ttest=\"$TEST_DIRECTORY/t4018/$what.sh\"\n+\tif ! test -e \"$test\"\n+\tthen\n+\t\ttest_expect_failure \"$what: no tests\" 'false'\n+\t\tcontinue\n+\tfi &&\n+\n+\ttest_expect_success \"setup: hunk header for $what\" '\n+\t\techo \"$what diff=$what\" >.gitattributes &&\n+\t\techo \"$what\" >arg.what\n+\t' &&\n+\n+\t. \"$test\"\n done\n \n test_done\ndiff --git a/t/t4018/README b/t/t4018/README\ndeleted file mode 100644\nindex d0619f76d4..0000000000\n--- a/t/t4018/README\n+++ /dev/null\n@@ -1,15 +0,0 @@\n-How to write test cases\n-=======================\n-\n-Create test cases called \"LANG-whatever\" in this directory, where\n-\"LANG\" is e.g. the userdiff driver name, where \"whatever\" is a brief\n-description of the test.\n-\n-Insert the word \"ChangeMe\" (exactly this form) at a distance of\n-at least two lines from the line that must appear in the hunk header.\n-\n-The text that must appear in the hunk header must contains the word\n-\"RIGHT\" by convention. The \"LANG-whatever.ctx\" file contains what we\n-expect to appear in the hunk header. We munged away the starting \"@@\n-[...] @@\" part of the line for ease of not having to hardcode the line\n-numbers and offsets.\ndiff --git a/t/t4018/README.ctx b/t/t4018/README.ctx\ndeleted file mode 100644\nindex cd79384b04..0000000000\n--- a/t/t4018/README.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-description of the test.\ndiff --git a/t/t4018/bash-arithmetic-function b/t/t4018/bash-arithmetic-function\ndeleted file mode 100644\nindex c0b276cb50..0000000000\n--- a/t/t4018/bash-arithmetic-function\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-RIGHT() ((\n-\n-    ChangeMe = \"$x\" + \"$y\"\n-))\ndiff --git a/t/t4018/bash-arithmetic-function.ctx b/t/t4018/bash-arithmetic-function.ctx\ndeleted file mode 100644\nindex 811eac7d2f..0000000000\n--- a/t/t4018/bash-arithmetic-function.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT()\ndiff --git a/t/t4018/bash-bashism-style-compact b/t/t4018/bash-bashism-style-compact\ndeleted file mode 100644\nindex 1ca3126f61..0000000000\n--- a/t/t4018/bash-bashism-style-compact\n+++ /dev/null\n@@ -1,6 +0,0 @@\n-function RIGHT {\n-    function InvalidSyntax{\n-        :\n-        echo 'ChangeMe'\n-    }\n-}\ndiff --git a/t/t4018/bash-bashism-style-compact.ctx b/t/t4018/bash-bashism-style-compact.ctx\ndeleted file mode 100644\nindex 4f8eac48c6..0000000000\n--- a/t/t4018/bash-bashism-style-compact.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-function RIGHT {\ndiff --git a/t/t4018/bash-bashism-style-function b/t/t4018/bash-bashism-style-function\ndeleted file mode 100644\nindex f1de4fa831..0000000000\n--- a/t/t4018/bash-bashism-style-function\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-function RIGHT {\n-    :\n-    echo 'ChangeMe'\n-}\ndiff --git a/t/t4018/bash-bashism-style-function.ctx b/t/t4018/bash-bashism-style-function.ctx\ndeleted file mode 100644\nindex 4f8eac48c6..0000000000\n--- a/t/t4018/bash-bashism-style-function.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-function RIGHT {\ndiff --git a/t/t4018/bash-bashism-style-whitespace b/t/t4018/bash-bashism-style-whitespace\ndeleted file mode 100644\nindex ade85dd3a5..0000000000\n--- a/t/t4018/bash-bashism-style-whitespace\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-\t function \tRIGHT \t( \t) \t{\n-\n-\t    ChangeMe\n-\t }\ndiff --git a/t/t4018/bash-bashism-style-whitespace.ctx b/t/t4018/bash-bashism-style-whitespace.ctx\ndeleted file mode 100644\nindex 35dbd0220e..0000000000\n--- a/t/t4018/bash-bashism-style-whitespace.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-function \tRIGHT \t( \t) \t{\ndiff --git a/t/t4018/bash-conditional-function b/t/t4018/bash-conditional-function\ndeleted file mode 100644\nindex c5949e829b..0000000000\n--- a/t/t4018/bash-conditional-function\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-RIGHT() [[ \\\n-\n-    \"$a\" > \"$ChangeMe\"\n-]]\ndiff --git a/t/t4018/bash-conditional-function.ctx b/t/t4018/bash-conditional-function.ctx\ndeleted file mode 100644\nindex 811eac7d2f..0000000000\n--- a/t/t4018/bash-conditional-function.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT()\ndiff --git a/t/t4018/bash-missing-parentheses b/t/t4018/bash-missing-parentheses\ndeleted file mode 100644\nindex 8c8a05dd7a..0000000000\n--- a/t/t4018/bash-missing-parentheses\n+++ /dev/null\n@@ -1,6 +0,0 @@\n-function RIGHT {\n-    functionInvalidSyntax {\n-        :\n-        echo 'ChangeMe'\n-    }\n-}\ndiff --git a/t/t4018/bash-missing-parentheses.ctx b/t/t4018/bash-missing-parentheses.ctx\ndeleted file mode 100644\nindex 4f8eac48c6..0000000000\n--- a/t/t4018/bash-missing-parentheses.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-function RIGHT {\ndiff --git a/t/t4018/bash-mixed-style-compact b/t/t4018/bash-mixed-style-compact\ndeleted file mode 100644\nindex d9364cba67..0000000000\n--- a/t/t4018/bash-mixed-style-compact\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-function RIGHT(){\n-    :\n-    echo 'ChangeMe'\n-}\ndiff --git a/t/t4018/bash-mixed-style-compact.ctx b/t/t4018/bash-mixed-style-compact.ctx\ndeleted file mode 100644\nindex bba11074eb..0000000000\n--- a/t/t4018/bash-mixed-style-compact.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-function RIGHT(){\ndiff --git a/t/t4018/bash-mixed-style-function b/t/t4018/bash-mixed-style-function\ndeleted file mode 100644\nindex 555f9b2466..0000000000\n--- a/t/t4018/bash-mixed-style-function\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-function RIGHT() {\n-\n-    ChangeMe\n-}\ndiff --git a/t/t4018/bash-mixed-style-function.ctx b/t/t4018/bash-mixed-style-function.ctx\ndeleted file mode 100644\nindex 922b87a4aa..0000000000\n--- a/t/t4018/bash-mixed-style-function.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-function RIGHT() {\ndiff --git a/t/t4018/bash-nested-functions b/t/t4018/bash-nested-functions\ndeleted file mode 100644\nindex 2c9237ead4..0000000000\n--- a/t/t4018/bash-nested-functions\n+++ /dev/null\n@@ -1,6 +0,0 @@\n-outer() {\n-    RIGHT() {\n-        :\n-        echo 'ChangeMe'\n-    }\n-}\ndiff --git a/t/t4018/bash-nested-functions.ctx b/t/t4018/bash-nested-functions.ctx\ndeleted file mode 100644\nindex 811eac7d2f..0000000000\n--- a/t/t4018/bash-nested-functions.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT()\ndiff --git a/t/t4018/bash-other-characters b/t/t4018/bash-other-characters\ndeleted file mode 100644\nindex a3f390d525..0000000000\n--- a/t/t4018/bash-other-characters\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-_RIGHT_0n() {\n-\n-    ChangeMe\n-}\ndiff --git a/t/t4018/bash-other-characters.ctx b/t/t4018/bash-other-characters.ctx\ndeleted file mode 100644\nindex 6a55317fdf..0000000000\n--- a/t/t4018/bash-other-characters.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-_RIGHT_0n()\ndiff --git a/t/t4018/bash-posix-style-compact b/t/t4018/bash-posix-style-compact\ndeleted file mode 100644\nindex 045bd2029b..0000000000\n--- a/t/t4018/bash-posix-style-compact\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-RIGHT(){\n-\n-    ChangeMe\n-}\ndiff --git a/t/t4018/bash-posix-style-compact.ctx b/t/t4018/bash-posix-style-compact.ctx\ndeleted file mode 100644\nindex 811eac7d2f..0000000000\n--- a/t/t4018/bash-posix-style-compact.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT()\ndiff --git a/t/t4018/bash-posix-style-function b/t/t4018/bash-posix-style-function\ndeleted file mode 100644\nindex a4d144856e..0000000000\n--- a/t/t4018/bash-posix-style-function\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-RIGHT() {\n-\n-    ChangeMe\n-}\ndiff --git a/t/t4018/bash-posix-style-function.ctx b/t/t4018/bash-posix-style-function.ctx\ndeleted file mode 100644\nindex 811eac7d2f..0000000000\n--- a/t/t4018/bash-posix-style-function.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT()\ndiff --git a/t/t4018/bash-posix-style-whitespace b/t/t4018/bash-posix-style-whitespace\ndeleted file mode 100644\nindex 4d984f0aa4..0000000000\n--- a/t/t4018/bash-posix-style-whitespace\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-\t RIGHT \t( \t) \t{\n-\n-\t    ChangeMe\n-\t }\ndiff --git a/t/t4018/bash-posix-style-whitespace.ctx b/t/t4018/bash-posix-style-whitespace.ctx\ndeleted file mode 100644\nindex 28f8698e14..0000000000\n--- a/t/t4018/bash-posix-style-whitespace.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT \t( \t)\ndiff --git a/t/t4018/bash-subshell-function b/t/t4018/bash-subshell-function\ndeleted file mode 100644\nindex 80baa09484..0000000000\n--- a/t/t4018/bash-subshell-function\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-RIGHT() (\n-\n-    ChangeMe=2\n-)\ndiff --git a/t/t4018/bash-subshell-function.ctx b/t/t4018/bash-subshell-function.ctx\ndeleted file mode 100644\nindex 811eac7d2f..0000000000\n--- a/t/t4018/bash-subshell-function.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT()\ndiff --git a/t/t4018/bash-trailing-comment b/t/t4018/bash-trailing-comment\ndeleted file mode 100644\nindex f1edbeda31..0000000000\n--- a/t/t4018/bash-trailing-comment\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-RIGHT() { # Comment\n-\n-    ChangeMe\n-}\ndiff --git a/t/t4018/bash-trailing-comment.ctx b/t/t4018/bash-trailing-comment.ctx\ndeleted file mode 100644\nindex 811eac7d2f..0000000000\n--- a/t/t4018/bash-trailing-comment.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT()\ndiff --git a/t/t4018/bash.sh b/t/t4018/bash.sh\nnew file mode 100755\nindex 0000000000..69144d9144\n--- /dev/null\n+++ b/t/t4018/bash.sh\n@@ -0,0 +1,160 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'bash: arithmetic function' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT()\n+EOF_HUNK\n+RIGHT() ((\n+\n+    ChangeMe = \"$x\" + \"$y\"\n+))\n+EOF_TEST\n+\n+test_diff_funcname 'bash: bashism style compact' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+function RIGHT {\n+EOF_HUNK\n+function RIGHT {\n+    function InvalidSyntax{\n+        :\n+        echo 'ChangeMe'\n+    }\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'bash: bashism style function' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+function RIGHT {\n+EOF_HUNK\n+function RIGHT {\n+    :\n+    echo 'ChangeMe'\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'bash: bashism style whitespace' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+function \tRIGHT \t( \t) \t{\n+EOF_HUNK\n+\t function \tRIGHT \t( \t) \t{\n+\n+\t    ChangeMe\n+\t }\n+EOF_TEST\n+\n+test_diff_funcname 'bash: conditional function' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT()\n+EOF_HUNK\n+RIGHT() [[ \\\n+\n+    \"$a\" > \"$ChangeMe\"\n+]]\n+EOF_TEST\n+\n+test_diff_funcname 'bash: missing parentheses' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+function RIGHT {\n+EOF_HUNK\n+function RIGHT {\n+    functionInvalidSyntax {\n+        :\n+        echo 'ChangeMe'\n+    }\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'bash: mixed style compact' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+function RIGHT(){\n+EOF_HUNK\n+function RIGHT(){\n+    :\n+    echo 'ChangeMe'\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'bash: mixed style function' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+function RIGHT() {\n+EOF_HUNK\n+function RIGHT() {\n+\n+    ChangeMe\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'bash: nested functions' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT()\n+EOF_HUNK\n+outer() {\n+    RIGHT() {\n+        :\n+        echo 'ChangeMe'\n+    }\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'bash: other characters' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+_RIGHT_0n()\n+EOF_HUNK\n+_RIGHT_0n() {\n+\n+    ChangeMe\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'bash: posix style compact' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT()\n+EOF_HUNK\n+RIGHT(){\n+\n+    ChangeMe\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'bash: posix style function' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT()\n+EOF_HUNK\n+RIGHT() {\n+\n+    ChangeMe\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'bash: posix style whitespace' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT \t( \t)\n+EOF_HUNK\n+\t RIGHT \t( \t) \t{\n+\n+\t    ChangeMe\n+\t }\n+EOF_TEST\n+\n+test_diff_funcname 'bash: subshell function' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT()\n+EOF_HUNK\n+RIGHT() (\n+\n+    ChangeMe=2\n+)\n+EOF_TEST\n+\n+test_diff_funcname 'bash: trailing comment' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT()\n+EOF_HUNK\n+RIGHT() { # Comment\n+\n+    ChangeMe\n+}\n+EOF_TEST\ndiff --git a/t/t4018/cpp-c++-function b/t/t4018/cpp-c++-function\ndeleted file mode 100644\nindex 9ee6bbef55..0000000000\n--- a/t/t4018/cpp-c++-function\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-Item RIGHT::DoSomething( Args with_spaces )\n-{\n-\tChangeMe;\n-}\ndiff --git a/t/t4018/cpp-c++-function.ctx b/t/t4018/cpp-c++-function.ctx\ndeleted file mode 100644\nindex 337b49dbb3..0000000000\n--- a/t/t4018/cpp-c++-function.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-Item RIGHT::DoSomething( Args with_spaces )\ndiff --git a/t/t4018/cpp-class-constructor b/t/t4018/cpp-class-constructor\ndeleted file mode 100644\nindex ec4f115c25..0000000000\n--- a/t/t4018/cpp-class-constructor\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-Item::Item(int RIGHT)\n-{\n-\tChangeMe;\n-}\ndiff --git a/t/t4018/cpp-class-constructor-mem-init b/t/t4018/cpp-class-constructor-mem-init\ndeleted file mode 100644\nindex 49a69f37e1..0000000000\n--- a/t/t4018/cpp-class-constructor-mem-init\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-Item::Item(int RIGHT) :\n-\tmember(0)\n-{\n-\tChangeMe;\n-}\ndiff --git a/t/t4018/cpp-class-constructor-mem-init.ctx b/t/t4018/cpp-class-constructor-mem-init.ctx\ndeleted file mode 100644\nindex 6664b8b3d8..0000000000\n--- a/t/t4018/cpp-class-constructor-mem-init.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-Item::Item(int RIGHT) :\ndiff --git a/t/t4018/cpp-class-constructor.ctx b/t/t4018/cpp-class-constructor.ctx\ndeleted file mode 100644\nindex 2dcadfc0ba..0000000000\n--- a/t/t4018/cpp-class-constructor.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-Item::Item(int RIGHT)\ndiff --git a/t/t4018/cpp-class-definition b/t/t4018/cpp-class-definition\ndeleted file mode 100644\nindex 11b61da3b7..0000000000\n--- a/t/t4018/cpp-class-definition\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-class RIGHT\n-{\n-\tint ChangeMe;\n-};\ndiff --git a/t/t4018/cpp-class-definition-derived b/t/t4018/cpp-class-definition-derived\ndeleted file mode 100644\nindex 3b98cd09ab..0000000000\n--- a/t/t4018/cpp-class-definition-derived\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-class RIGHT :\n-\tpublic Baseclass\n-{\n-\tint ChangeMe;\n-};\ndiff --git a/t/t4018/cpp-class-definition-derived.ctx b/t/t4018/cpp-class-definition-derived.ctx\ndeleted file mode 100644\nindex 146f0a7b7c..0000000000\n--- a/t/t4018/cpp-class-definition-derived.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-class RIGHT :\ndiff --git a/t/t4018/cpp-class-definition.ctx b/t/t4018/cpp-class-definition.ctx\ndeleted file mode 100644\nindex 54bff816d6..0000000000\n--- a/t/t4018/cpp-class-definition.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-class RIGHT\ndiff --git a/t/t4018/cpp-class-destructor b/t/t4018/cpp-class-destructor\ndeleted file mode 100644\nindex 5487665096..0000000000\n--- a/t/t4018/cpp-class-destructor\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-RIGHT::~RIGHT()\n-{\n-\tChangeMe;\n-}\ndiff --git a/t/t4018/cpp-class-destructor.ctx b/t/t4018/cpp-class-destructor.ctx\ndeleted file mode 100644\nindex 5390c17cf6..0000000000\n--- a/t/t4018/cpp-class-destructor.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT::~RIGHT()\ndiff --git a/t/t4018/cpp-function-returning-global-type b/t/t4018/cpp-function-returning-global-type\ndeleted file mode 100644\nindex 1084d5990e..0000000000\n--- a/t/t4018/cpp-function-returning-global-type\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-::Item get::it::RIGHT()\n-{\n-\tChangeMe;\n-}\ndiff --git a/t/t4018/cpp-function-returning-global-type.ctx b/t/t4018/cpp-function-returning-global-type.ctx\ndeleted file mode 100644\nindex 4dcdde25f4..0000000000\n--- a/t/t4018/cpp-function-returning-global-type.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-::Item get::it::RIGHT()\ndiff --git a/t/t4018/cpp-function-returning-nested b/t/t4018/cpp-function-returning-nested\ndeleted file mode 100644\nindex d9750aa61a..0000000000\n--- a/t/t4018/cpp-function-returning-nested\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-get::Item get::it::RIGHT()\n-{\n-\tChangeMe;\n-}\n-\ndiff --git a/t/t4018/cpp-function-returning-nested.ctx b/t/t4018/cpp-function-returning-nested.ctx\ndeleted file mode 100644\nindex 6ef73c8368..0000000000\n--- a/t/t4018/cpp-function-returning-nested.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-get::Item get::it::RIGHT()\ndiff --git a/t/t4018/cpp-function-returning-pointer b/t/t4018/cpp-function-returning-pointer\ndeleted file mode 100644\nindex ef15657ea8..0000000000\n--- a/t/t4018/cpp-function-returning-pointer\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-const char *get_it_RIGHT(char *ptr)\n-{\n-\tChangeMe;\n-}\ndiff --git a/t/t4018/cpp-function-returning-pointer.ctx b/t/t4018/cpp-function-returning-pointer.ctx\ndeleted file mode 100644\nindex bb0acce5c7..0000000000\n--- a/t/t4018/cpp-function-returning-pointer.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-const char *get_it_RIGHT(char *ptr)\ndiff --git a/t/t4018/cpp-function-returning-reference b/t/t4018/cpp-function-returning-reference\ndeleted file mode 100644\nindex 01b051df70..0000000000\n--- a/t/t4018/cpp-function-returning-reference\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-string& get::it::RIGHT(char *ptr)\n-{\n-\tChangeMe;\n-}\ndiff --git a/t/t4018/cpp-function-returning-reference.ctx b/t/t4018/cpp-function-returning-reference.ctx\ndeleted file mode 100644\nindex 76afe381fd..0000000000\n--- a/t/t4018/cpp-function-returning-reference.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-string& get::it::RIGHT(char *ptr)\ndiff --git a/t/t4018/cpp-gnu-style-function b/t/t4018/cpp-gnu-style-function\ndeleted file mode 100644\nindex 08c7c7565a..0000000000\n--- a/t/t4018/cpp-gnu-style-function\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-const char *\n-RIGHT(int arg)\n-{\n-\tChangeMe;\n-}\ndiff --git a/t/t4018/cpp-gnu-style-function.ctx b/t/t4018/cpp-gnu-style-function.ctx\ndeleted file mode 100644\nindex 1858287812..0000000000\n--- a/t/t4018/cpp-gnu-style-function.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT(int arg)\ndiff --git a/t/t4018/cpp-namespace-definition b/t/t4018/cpp-namespace-definition\ndeleted file mode 100644\nindex 6749980241..0000000000\n--- a/t/t4018/cpp-namespace-definition\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-namespace RIGHT\n-{\n-\tChangeMe;\n-}\ndiff --git a/t/t4018/cpp-namespace-definition.ctx b/t/t4018/cpp-namespace-definition.ctx\ndeleted file mode 100644\nindex 14c29c4638..0000000000\n--- a/t/t4018/cpp-namespace-definition.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-namespace RIGHT\ndiff --git a/t/t4018/cpp-operator-definition b/t/t4018/cpp-operator-definition\ndeleted file mode 100644\nindex 1acd827159..0000000000\n--- a/t/t4018/cpp-operator-definition\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-Value operator+(Value LEFT, Value RIGHT)\n-{\n-\tChangeMe;\n-}\ndiff --git a/t/t4018/cpp-operator-definition.ctx b/t/t4018/cpp-operator-definition.ctx\ndeleted file mode 100644\nindex 5b56778961..0000000000\n--- a/t/t4018/cpp-operator-definition.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-Value operator+(Value LEFT, Value RIGHT)\ndiff --git a/t/t4018/cpp-skip-access-specifiers b/t/t4018/cpp-skip-access-specifiers\ndeleted file mode 100644\nindex 4d4a9dbb9d..0000000000\n--- a/t/t4018/cpp-skip-access-specifiers\n+++ /dev/null\n@@ -1,8 +0,0 @@\n-class RIGHT : public Baseclass\n-{\n-public:\n-protected:\n-private:\n-\tvoid DoSomething();\n-\tint ChangeMe;\n-};\ndiff --git a/t/t4018/cpp-skip-access-specifiers.ctx b/t/t4018/cpp-skip-access-specifiers.ctx\ndeleted file mode 100644\nindex 075bcd883b..0000000000\n--- a/t/t4018/cpp-skip-access-specifiers.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-class RIGHT : public Baseclass\ndiff --git a/t/t4018/cpp-skip-comment-block b/t/t4018/cpp-skip-comment-block\ndeleted file mode 100644\nindex 3800b9967a..0000000000\n--- a/t/t4018/cpp-skip-comment-block\n+++ /dev/null\n@@ -1,9 +0,0 @@\n-struct item RIGHT(int i)\n-// Do not\n-// pick up\n-/* these\n-** comments.\n-*/\n-{\n-\tChangeMe;\n-}\ndiff --git a/t/t4018/cpp-skip-comment-block.ctx b/t/t4018/cpp-skip-comment-block.ctx\ndeleted file mode 100644\nindex 656c59893d..0000000000\n--- a/t/t4018/cpp-skip-comment-block.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-struct item RIGHT(int i)\ndiff --git a/t/t4018/cpp-skip-labels b/t/t4018/cpp-skip-labels\ndeleted file mode 100644\nindex b9c10aba22..0000000000\n--- a/t/t4018/cpp-skip-labels\n+++ /dev/null\n@@ -1,8 +0,0 @@\n-void RIGHT (void)\n-{\n-repeat:\t\t// C++ comment\n-next:\t\t/* C comment */\n-\tdo_something();\n-\n-\tChangeMe;\n-}\ndiff --git a/t/t4018/cpp-skip-labels.ctx b/t/t4018/cpp-skip-labels.ctx\ndeleted file mode 100644\nindex 6b0635f7f7..0000000000\n--- a/t/t4018/cpp-skip-labels.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-void RIGHT (void)\ndiff --git a/t/t4018/cpp-struct-definition b/t/t4018/cpp-struct-definition\ndeleted file mode 100644\nindex 521c59fd15..0000000000\n--- a/t/t4018/cpp-struct-definition\n+++ /dev/null\n@@ -1,9 +0,0 @@\n-struct RIGHT {\n-\tunsigned\n-\t/* this bit field looks like a label and should not be picked up */\n-\t\tdecoy_bitfield: 2,\n-\t\tmore : 1;\n-\tint filler;\n-\n-\tint ChangeMe;\n-};\ndiff --git a/t/t4018/cpp-struct-definition.ctx b/t/t4018/cpp-struct-definition.ctx\ndeleted file mode 100644\nindex 48ed893279..0000000000\n--- a/t/t4018/cpp-struct-definition.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-struct RIGHT {\ndiff --git a/t/t4018/cpp-struct-single-line b/t/t4018/cpp-struct-single-line\ndeleted file mode 100644\nindex a0de5fb800..0000000000\n--- a/t/t4018/cpp-struct-single-line\n+++ /dev/null\n@@ -1,7 +0,0 @@\n-void wrong()\n-{\n-}\n-\n-struct RIGHT_iterator_tag {};\n-\n-int ChangeMe;\ndiff --git a/t/t4018/cpp-struct-single-line.ctx b/t/t4018/cpp-struct-single-line.ctx\ndeleted file mode 100644\nindex e3bc9d5017..0000000000\n--- a/t/t4018/cpp-struct-single-line.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-struct RIGHT_iterator_tag {};\ndiff --git a/t/t4018/cpp-template-function-definition b/t/t4018/cpp-template-function-definition\ndeleted file mode 100644\nindex 0cdf5ba5bd..0000000000\n--- a/t/t4018/cpp-template-function-definition\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-template<class T> int RIGHT(T arg)\n-{\n-\tChangeMe;\n-}\ndiff --git a/t/t4018/cpp-template-function-definition.ctx b/t/t4018/cpp-template-function-definition.ctx\ndeleted file mode 100644\nindex c9da39cf65..0000000000\n--- a/t/t4018/cpp-template-function-definition.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-template<class T> int RIGHT(T arg)\ndiff --git a/t/t4018/cpp-union-definition b/t/t4018/cpp-union-definition\ndeleted file mode 100644\nindex 7ec94df697..0000000000\n--- a/t/t4018/cpp-union-definition\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-union RIGHT {\n-\tdouble v;\n-\tint ChangeMe;\n-};\ndiff --git a/t/t4018/cpp-union-definition.ctx b/t/t4018/cpp-union-definition.ctx\ndeleted file mode 100644\nindex 2fc7b54fb8..0000000000\n--- a/t/t4018/cpp-union-definition.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-union RIGHT {\ndiff --git a/t/t4018/cpp-void-c-function b/t/t4018/cpp-void-c-function\ndeleted file mode 100644\nindex 153081e872..0000000000\n--- a/t/t4018/cpp-void-c-function\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-void RIGHT (void)\n-{\n-\tChangeMe;\n-}\ndiff --git a/t/t4018/cpp-void-c-function.ctx b/t/t4018/cpp-void-c-function.ctx\ndeleted file mode 100644\nindex 6b0635f7f7..0000000000\n--- a/t/t4018/cpp-void-c-function.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-void RIGHT (void)\ndiff --git a/t/t4018/cpp.sh b/t/t4018/cpp.sh\nnew file mode 100755\nindex 0000000000..185d40d5ef\n--- /dev/null\n+++ b/t/t4018/cpp.sh\n@@ -0,0 +1,239 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'cpp: c++ function' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+Item RIGHT::DoSomething( Args with_spaces )\n+EOF_HUNK\n+Item RIGHT::DoSomething( Args with_spaces )\n+{\n+\tChangeMe;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'cpp: class constructor' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+Item::Item(int RIGHT)\n+EOF_HUNK\n+Item::Item(int RIGHT)\n+{\n+\tChangeMe;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'cpp: class constructor mem init' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+Item::Item(int RIGHT) :\n+EOF_HUNK\n+Item::Item(int RIGHT) :\n+\tmember(0)\n+{\n+\tChangeMe;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'cpp: class definition' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+class RIGHT\n+EOF_HUNK\n+class RIGHT\n+{\n+\tint ChangeMe;\n+};\n+EOF_TEST\n+\n+test_diff_funcname 'cpp: class definition derived' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+class RIGHT :\n+EOF_HUNK\n+class RIGHT :\n+\tpublic Baseclass\n+{\n+\tint ChangeMe;\n+};\n+EOF_TEST\n+\n+test_diff_funcname 'cpp: class destructor' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT::~RIGHT()\n+EOF_HUNK\n+RIGHT::~RIGHT()\n+{\n+\tChangeMe;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'cpp: function returning global type' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+::Item get::it::RIGHT()\n+EOF_HUNK\n+::Item get::it::RIGHT()\n+{\n+\tChangeMe;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'cpp: function returning nested' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+get::Item get::it::RIGHT()\n+EOF_HUNK\n+get::Item get::it::RIGHT()\n+{\n+\tChangeMe;\n+}\n+\n+EOF_TEST\n+\n+test_diff_funcname 'cpp: function returning pointer' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+const char *get_it_RIGHT(char *ptr)\n+EOF_HUNK\n+const char *get_it_RIGHT(char *ptr)\n+{\n+\tChangeMe;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'cpp: function returning reference' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+string& get::it::RIGHT(char *ptr)\n+EOF_HUNK\n+string& get::it::RIGHT(char *ptr)\n+{\n+\tChangeMe;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'cpp: gnu style function' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT(int arg)\n+EOF_HUNK\n+const char *\n+RIGHT(int arg)\n+{\n+\tChangeMe;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'cpp: namespace definition' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+namespace RIGHT\n+EOF_HUNK\n+namespace RIGHT\n+{\n+\tChangeMe;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'cpp: operator definition' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+Value operator+(Value LEFT, Value RIGHT)\n+EOF_HUNK\n+Value operator+(Value LEFT, Value RIGHT)\n+{\n+\tChangeMe;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'cpp: skip access specifiers' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+class RIGHT : public Baseclass\n+EOF_HUNK\n+class RIGHT : public Baseclass\n+{\n+public:\n+protected:\n+private:\n+\tvoid DoSomething();\n+\tint ChangeMe;\n+};\n+EOF_TEST\n+\n+test_diff_funcname 'cpp: skip comment block' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+struct item RIGHT(int i)\n+EOF_HUNK\n+struct item RIGHT(int i)\n+// Do not\n+// pick up\n+/* these\n+** comments.\n+*/\n+{\n+\tChangeMe;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'cpp: skip labels' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+void RIGHT (void)\n+EOF_HUNK\n+void RIGHT (void)\n+{\n+repeat:\t\t// C++ comment\n+next:\t\t/* C comment */\n+\tdo_something();\n+\n+\tChangeMe;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'cpp: struct definition' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+struct RIGHT {\n+EOF_HUNK\n+struct RIGHT {\n+\tunsigned\n+\t/* this bit field looks like a label and should not be picked up */\n+\t\tdecoy_bitfield: 2,\n+\t\tmore : 1;\n+\tint filler;\n+\n+\tint ChangeMe;\n+};\n+EOF_TEST\n+\n+test_diff_funcname 'cpp: struct single line' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+struct RIGHT_iterator_tag {};\n+EOF_HUNK\n+void wrong()\n+{\n+}\n+\n+struct RIGHT_iterator_tag {};\n+\n+int ChangeMe;\n+EOF_TEST\n+\n+test_diff_funcname 'cpp: template function definition' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+template<class T> int RIGHT(T arg)\n+EOF_HUNK\n+template<class T> int RIGHT(T arg)\n+{\n+\tChangeMe;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'cpp: union definition' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+union RIGHT {\n+EOF_HUNK\n+union RIGHT {\n+\tdouble v;\n+\tint ChangeMe;\n+};\n+EOF_TEST\n+\n+test_diff_funcname 'cpp: void c function' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+void RIGHT (void)\n+EOF_HUNK\n+void RIGHT (void)\n+{\n+\tChangeMe;\n+}\n+EOF_TEST\ndiff --git a/t/t4018/css-attribute-value-selector b/t/t4018/css-attribute-value-selector\ndeleted file mode 100644\nindex 918256b20c..0000000000\n--- a/t/t4018/css-attribute-value-selector\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-[class*=\"RIGHT\"] {\n-    background : #000;\n-    border : 10px ChangeMe #C6C6C6;\n-}\ndiff --git a/t/t4018/css-attribute-value-selector.ctx b/t/t4018/css-attribute-value-selector.ctx\ndeleted file mode 100644\nindex 7f8956251c..0000000000\n--- a/t/t4018/css-attribute-value-selector.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-[class*=\"RIGHT\"] {\ndiff --git a/t/t4018/css-block-level-@-statements b/t/t4018/css-block-level-@-statements\ndeleted file mode 100644\nindex d6755f2f3d..0000000000\n--- a/t/t4018/css-block-level-@-statements\n+++ /dev/null\n@@ -1,10 +0,0 @@\n-@keyframes RIGHT {\n-    from {\n-        background : #000;\n-        border : 10px ChangeMe #C6C6C6;\n-    }\n-    to {\n-        background : #fff;\n-        border : 10px solid #C6C6C6;\n-    }\n-}\ndiff --git a/t/t4018/css-block-level-@-statements.ctx b/t/t4018/css-block-level-@-statements.ctx\ndeleted file mode 100644\nindex 7f5e90468c..0000000000\n--- a/t/t4018/css-block-level-@-statements.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-@keyframes RIGHT {\ndiff --git a/t/t4018/css-brace-in-col-1 b/t/t4018/css-brace-in-col-1\ndeleted file mode 100644\nindex 7831577506..0000000000\n--- a/t/t4018/css-brace-in-col-1\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-RIGHT label.control-label\n-{\n-    margin-top: 10px!important;\n-    border : 10px ChangeMe #C6C6C6;\n-}\ndiff --git a/t/t4018/css-brace-in-col-1.ctx b/t/t4018/css-brace-in-col-1.ctx\ndeleted file mode 100644\nindex 91a9105c6a..0000000000\n--- a/t/t4018/css-brace-in-col-1.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT label.control-label\ndiff --git a/t/t4018/css-class-selector b/t/t4018/css-class-selector\ndeleted file mode 100644\nindex f790a0062f..0000000000\n--- a/t/t4018/css-class-selector\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-.RIGHT {\n-    background : #000;\n-    border : 10px ChangeMe #C6C6C6;\n-}\ndiff --git a/t/t4018/css-class-selector.ctx b/t/t4018/css-class-selector.ctx\ndeleted file mode 100644\nindex ac7367d7f4..0000000000\n--- a/t/t4018/css-class-selector.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-.RIGHT {\ndiff --git a/t/t4018/css-colon-eol b/t/t4018/css-colon-eol\ndeleted file mode 100644\nindex 5a30553d29..0000000000\n--- a/t/t4018/css-colon-eol\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-RIGHT h1 {\n-color:\n-ChangeMe;\n-}\ndiff --git a/t/t4018/css-colon-eol.ctx b/t/t4018/css-colon-eol.ctx\ndeleted file mode 100644\nindex b68493b9b0..0000000000\n--- a/t/t4018/css-colon-eol.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT h1 {\ndiff --git a/t/t4018/css-colon-selector b/t/t4018/css-colon-selector\ndeleted file mode 100644\nindex c6d71fb42d..0000000000\n--- a/t/t4018/css-colon-selector\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-RIGHT a:hover {\n-    margin-top:\n-    10px!important;\n-    border : 10px ChangeMe #C6C6C6;\n-}\ndiff --git a/t/t4018/css-colon-selector.ctx b/t/t4018/css-colon-selector.ctx\ndeleted file mode 100644\nindex 00b1a5aefe..0000000000\n--- a/t/t4018/css-colon-selector.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT a:hover {\ndiff --git a/t/t4018/css-common b/t/t4018/css-common\ndeleted file mode 100644\nindex 84ed754b33..0000000000\n--- a/t/t4018/css-common\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-RIGHT label.control-label {\n-    margin-top: 10px!important;\n-    border : 10px ChangeMe #C6C6C6;\n-}\ndiff --git a/t/t4018/css-common.ctx b/t/t4018/css-common.ctx\ndeleted file mode 100644\nindex 43686b4081..0000000000\n--- a/t/t4018/css-common.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT label.control-label {\ndiff --git a/t/t4018/css-id-selector b/t/t4018/css-id-selector\ndeleted file mode 100644\nindex 17c5111052..0000000000\n--- a/t/t4018/css-id-selector\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-#RIGHT {\n-    background : #000;\n-    border : 10px ChangeMe #C6C6C6;\n-}\ndiff --git a/t/t4018/css-id-selector.ctx b/t/t4018/css-id-selector.ctx\ndeleted file mode 100644\nindex ce19f6d8dc..0000000000\n--- a/t/t4018/css-id-selector.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-#RIGHT {\ndiff --git a/t/t4018/css-long-selector-list b/t/t4018/css-long-selector-list\ndeleted file mode 100644\nindex 7ccd25d9ed..0000000000\n--- a/t/t4018/css-long-selector-list\n+++ /dev/null\n@@ -1,6 +0,0 @@\n-p.header,\n-label.control-label,\n-div ul#RIGHT {\n-    margin-top: 10px!important;\n-    border : 10px ChangeMe #C6C6C6;\n-}\ndiff --git a/t/t4018/css-long-selector-list.ctx b/t/t4018/css-long-selector-list.ctx\ndeleted file mode 100644\nindex bc8d0fb62c..0000000000\n--- a/t/t4018/css-long-selector-list.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-div ul#RIGHT {\ndiff --git a/t/t4018/css-prop-sans-indent b/t/t4018/css-prop-sans-indent\ndeleted file mode 100644\nindex a9e3c86b3c..0000000000\n--- a/t/t4018/css-prop-sans-indent\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-RIGHT, label.control-label {\n-margin-top: 10px!important;\n-padding: 0;\n-border : 10px ChangeMe #C6C6C6;\n-}\ndiff --git a/t/t4018/css-prop-sans-indent.ctx b/t/t4018/css-prop-sans-indent.ctx\ndeleted file mode 100644\nindex cc880b2f44..0000000000\n--- a/t/t4018/css-prop-sans-indent.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT, label.control-label {\ndiff --git a/t/t4018/css-root-selector b/t/t4018/css-root-selector\ndeleted file mode 100644\nindex 22b958e369..0000000000\n--- a/t/t4018/css-root-selector\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-:RIGHT {\n-    background : #000;\n-    border : 10px ChangeMe #C6C6C6;\n-}\ndiff --git a/t/t4018/css-root-selector.ctx b/t/t4018/css-root-selector.ctx\ndeleted file mode 100644\nindex 3010cded2a..0000000000\n--- a/t/t4018/css-root-selector.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-:RIGHT {\ndiff --git a/t/t4018/css-short-selector-list b/t/t4018/css-short-selector-list\ndeleted file mode 100644\nindex 6a0bdee336..0000000000\n--- a/t/t4018/css-short-selector-list\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-label.control, div ul#RIGHT {\n-    margin-top: 10px!important;\n-    border : 10px ChangeMe #C6C6C6;\n-}\ndiff --git a/t/t4018/css-short-selector-list.ctx b/t/t4018/css-short-selector-list.ctx\ndeleted file mode 100644\nindex 9e5d87d126..0000000000\n--- a/t/t4018/css-short-selector-list.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-label.control, div ul#RIGHT {\ndiff --git a/t/t4018/css-trailing-space b/t/t4018/css-trailing-space\ndeleted file mode 100644\nindex 32b5606c70..0000000000\n--- a/t/t4018/css-trailing-space\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-RIGHT label.control-label {\n-    margin:10px;   \n-    padding:10px;\n-    border : 10px ChangeMe #C6C6C6;\n-}\ndiff --git a/t/t4018/css-trailing-space.ctx b/t/t4018/css-trailing-space.ctx\ndeleted file mode 100644\nindex 43686b4081..0000000000\n--- a/t/t4018/css-trailing-space.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT label.control-label {\ndiff --git a/t/t4018/css.sh b/t/t4018/css.sh\nnew file mode 100755\nindex 0000000000..106a3de242\n--- /dev/null\n+++ b/t/t4018/css.sh\n@@ -0,0 +1,146 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'css: attribute value selector' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+[class*=\"RIGHT\"] {\n+EOF_HUNK\n+[class*=\"RIGHT\"] {\n+    background : #000;\n+    border : 10px ChangeMe #C6C6C6;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'css: block level @ statements' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+@keyframes RIGHT {\n+EOF_HUNK\n+@keyframes RIGHT {\n+    from {\n+        background : #000;\n+        border : 10px ChangeMe #C6C6C6;\n+    }\n+    to {\n+        background : #fff;\n+        border : 10px solid #C6C6C6;\n+    }\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'css: brace in col 1' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT label.control-label\n+EOF_HUNK\n+RIGHT label.control-label\n+{\n+    margin-top: 10px!important;\n+    border : 10px ChangeMe #C6C6C6;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'css: class selector' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+.RIGHT {\n+EOF_HUNK\n+.RIGHT {\n+    background : #000;\n+    border : 10px ChangeMe #C6C6C6;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'css: colon eol' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT h1 {\n+EOF_HUNK\n+RIGHT h1 {\n+color:\n+ChangeMe;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'css: colon selector' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT a:hover {\n+EOF_HUNK\n+RIGHT a:hover {\n+    margin-top:\n+    10px!important;\n+    border : 10px ChangeMe #C6C6C6;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'css: common' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT label.control-label {\n+EOF_HUNK\n+RIGHT label.control-label {\n+    margin-top: 10px!important;\n+    border : 10px ChangeMe #C6C6C6;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'css: id selector' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+#RIGHT {\n+EOF_HUNK\n+#RIGHT {\n+    background : #000;\n+    border : 10px ChangeMe #C6C6C6;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'css: long selector list' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+div ul#RIGHT {\n+EOF_HUNK\n+p.header,\n+label.control-label,\n+div ul#RIGHT {\n+    margin-top: 10px!important;\n+    border : 10px ChangeMe #C6C6C6;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'css: prop sans indent' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT, label.control-label {\n+EOF_HUNK\n+RIGHT, label.control-label {\n+margin-top: 10px!important;\n+padding: 0;\n+border : 10px ChangeMe #C6C6C6;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'css: root selector' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+:RIGHT {\n+EOF_HUNK\n+:RIGHT {\n+    background : #000;\n+    border : 10px ChangeMe #C6C6C6;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'css: short selector list' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+label.control, div ul#RIGHT {\n+EOF_HUNK\n+label.control, div ul#RIGHT {\n+    margin-top: 10px!important;\n+    border : 10px ChangeMe #C6C6C6;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'css: trailing space' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT label.control-label {\n+EOF_HUNK\n+RIGHT label.control-label {\n+    margin:10px;   \n+    padding:10px;\n+    border : 10px ChangeMe #C6C6C6;\n+}\n+EOF_TEST\ndiff --git a/t/t4018/custom1-pattern.ctx b/t/t4018/custom1-pattern.ctx\ndeleted file mode 100644\nindex d1609cc9a6..0000000000\n--- a/t/t4018/custom1-pattern.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-int special, RIGHT;\ndiff --git a/t/t4018/custom1-pattern b/t/t4018/custom1.sh\nold mode 100644\nnew mode 100755\nsimilarity index 71%\nrename from t/t4018/custom1-pattern\nrename to t/t4018/custom1.sh\nindex e8fd59f884..f8bbccadb4\n--- a/t/t4018/custom1-pattern\n+++ b/t/t4018/custom1.sh\n@@ -1,3 +1,12 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'custom1: pattern' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+int special, RIGHT;\n+EOF_HUNK\n public class Beer\n {\n \tint special, RIGHT;\n@@ -15,3 +24,4 @@ public class Beer\n \t\t\t+ \"99 bottles of beer on the wall.\\n\");\n \t}\n }\n+EOF_TEST\ndiff --git a/t/t4018/custom2-match-to-end-of-line b/t/t4018/custom2-match-to-end-of-line\ndeleted file mode 100644\nindex f88ac318b7..0000000000\n--- a/t/t4018/custom2-match-to-end-of-line\n+++ /dev/null\n@@ -1,8 +0,0 @@\n-public class RIGHT_Beer\n-{\n-\tint special;\n-\tpublic static void main(String args[])\n-\t{\n-\t\tSystem.out.print(\"ChangeMe\");\n-\t}\n-}\ndiff --git a/t/t4018/custom2-match-to-end-of-line.ctx b/t/t4018/custom2-match-to-end-of-line.ctx\ndeleted file mode 100644\nindex 8294c6e49b..0000000000\n--- a/t/t4018/custom2-match-to-end-of-line.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT_Beer\ndiff --git a/t/t4018/custom2.sh b/t/t4018/custom2.sh\nnew file mode 100755\nindex 0000000000..c68421f788\n--- /dev/null\n+++ b/t/t4018/custom2.sh\n@@ -0,0 +1,18 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'custom2: match to end of line' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT_Beer\n+EOF_HUNK\n+public class RIGHT_Beer\n+{\n+\tint special;\n+\tpublic static void main(String args[])\n+\t{\n+\t\tSystem.out.print(\"ChangeMe\");\n+\t}\n+}\n+EOF_TEST\ndiff --git a/t/t4018/custom3-alternation-in-pattern.ctx b/t/t4018/custom3-alternation-in-pattern.ctx\ndeleted file mode 100644\nindex 2125474b68..0000000000\n--- a/t/t4018/custom3-alternation-in-pattern.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-public static void main(String RIGHT[])\ndiff --git a/t/t4018/custom3-alternation-in-pattern b/t/t4018/custom3.sh\nold mode 100644\nnew mode 100755\nsimilarity index 66%\nrename from t/t4018/custom3-alternation-in-pattern\nrename to t/t4018/custom3.sh\nindex 5f3769c64f..07c5c134ff\n--- a/t/t4018/custom3-alternation-in-pattern\n+++ b/t/t4018/custom3.sh\n@@ -1,3 +1,12 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'custom3: alternation in pattern' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+public static void main(String RIGHT[])\n+EOF_HUNK\n public class Beer\n {\n \tint special;\n@@ -15,3 +24,4 @@ public class Beer\n \t\t\t+ \"99 bottles of beer on the wall.\\n\");\n \t}\n }\n+EOF_TEST\ndiff --git a/t/t4018/dts-labels b/t/t4018/dts-labels\ndeleted file mode 100644\nindex b21ef8737b..0000000000\n--- a/t/t4018/dts-labels\n+++ /dev/null\n@@ -1,9 +0,0 @@\n-/ {\n-\tlabel_1: node1@ff00 {\n-\t\tlabel2: RIGHT {\n-\t\t\tvendor,some-property;\n-\n-\t\t\tChangeMe = <0x45-30>;\n-\t\t};\n-\t};\n-};\ndiff --git a/t/t4018/dts-labels.ctx b/t/t4018/dts-labels.ctx\ndeleted file mode 100644\nindex 48d9373cab..0000000000\n--- a/t/t4018/dts-labels.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-label2: RIGHT {\ndiff --git a/t/t4018/dts-node-unitless b/t/t4018/dts-node-unitless\ndeleted file mode 100644\nindex c5287d9141..0000000000\n--- a/t/t4018/dts-node-unitless\n+++ /dev/null\n@@ -1,8 +0,0 @@\n-/ {\n-\tlabel_1: node1 {\n-\t\tRIGHT {\n-\t\t\tprop-array = <1>, <4>;\n-\t\t\tChangeMe = <0xffeedd00>;\n-\t\t};\n-\t};\n-};\ndiff --git a/t/t4018/dts-node-unitless.ctx b/t/t4018/dts-node-unitless.ctx\ndeleted file mode 100644\nindex 82c8683fa1..0000000000\n--- a/t/t4018/dts-node-unitless.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT {\ndiff --git a/t/t4018/dts-nodes b/t/t4018/dts-nodes\ndeleted file mode 100644\nindex 5a4334bb16..0000000000\n--- a/t/t4018/dts-nodes\n+++ /dev/null\n@@ -1,8 +0,0 @@\n-/ {\n-\tlabel_1: node1@ff00 {\n-\t\tRIGHT@deadf00,4000 {\n-\t\t\t#size-cells = <1>;\n-\t\t\tChangeMe = <0xffeedd00>;\n-\t\t};\n-\t};\n-};\ndiff --git a/t/t4018/dts-nodes-boolean-prop b/t/t4018/dts-nodes-boolean-prop\ndeleted file mode 100644\nindex afc6b5b404..0000000000\n--- a/t/t4018/dts-nodes-boolean-prop\n+++ /dev/null\n@@ -1,9 +0,0 @@\n-/ {\n-\tlabel_1: node1@ff00 {\n-\t\tRIGHT@deadf00,4000 {\n-\t\t\tboolean-prop1;\n-\n-\t\t\tChangeMe;\n-\t\t};\n-\t};\n-};\ndiff --git a/t/t4018/dts-nodes-boolean-prop.ctx b/t/t4018/dts-nodes-boolean-prop.ctx\ndeleted file mode 100644\nindex 3a0232d55d..0000000000\n--- a/t/t4018/dts-nodes-boolean-prop.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT@deadf00,4000 {\ndiff --git a/t/t4018/dts-nodes-comment1 b/t/t4018/dts-nodes-comment1\ndeleted file mode 100644\nindex 559dfce9b3..0000000000\n--- a/t/t4018/dts-nodes-comment1\n+++ /dev/null\n@@ -1,8 +0,0 @@\n-/ {\n-\tlabel_1: node1@ff00 {\n-\t\tRIGHT@deadf00,4000 /* &a comment */ {\n-\t\t\t#size-cells = <1>;\n-\t\t\tChangeMe = <0xffeedd00>;\n-\t\t};\n-\t};\n-};\ndiff --git a/t/t4018/dts-nodes-comment1.ctx b/t/t4018/dts-nodes-comment1.ctx\ndeleted file mode 100644\nindex ec364600b1..0000000000\n--- a/t/t4018/dts-nodes-comment1.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT@deadf00,4000 /* &a comment */ {\ndiff --git a/t/t4018/dts-nodes-comment2 b/t/t4018/dts-nodes-comment2\ndeleted file mode 100644\nindex 27e9718b31..0000000000\n--- a/t/t4018/dts-nodes-comment2\n+++ /dev/null\n@@ -1,8 +0,0 @@\n-/ {\n-\tlabel_1: node1@ff00 {\n-\t\tRIGHT@deadf00,4000 { /* a trailing comment */ \n-\t\t\t#size-cells = <1>;\n-\t\t\tChangeMe = <0xffeedd00>;\n-\t\t};\n-\t};\n-};\ndiff --git a/t/t4018/dts-nodes-comment2.ctx b/t/t4018/dts-nodes-comment2.ctx\ndeleted file mode 100644\nindex 75f0d75258..0000000000\n--- a/t/t4018/dts-nodes-comment2.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT@deadf00,4000 { /* a trailing comment */\ndiff --git a/t/t4018/dts-nodes-multiline-prop b/t/t4018/dts-nodes-multiline-prop\ndeleted file mode 100644\nindex 072d58b69d..0000000000\n--- a/t/t4018/dts-nodes-multiline-prop\n+++ /dev/null\n@@ -1,13 +0,0 @@\n-/ {\n-\tlabel_1: node1@ff00 {\n-\t\tRIGHT@deadf00,4000 {\n-\t\t\tmultilineprop = <3>,\n-\t\t\t\t\t<4>,\n-\t\t\t\t\t<5>,\n-\t\t\t\t\t<6>,\n-\t\t\t\t\t<7>;\n-\n-\t\t\tChangeMe = <0xffeedd00>;\n-\t\t};\n-\t};\n-};\ndiff --git a/t/t4018/dts-nodes-multiline-prop.ctx b/t/t4018/dts-nodes-multiline-prop.ctx\ndeleted file mode 100644\nindex 3a0232d55d..0000000000\n--- a/t/t4018/dts-nodes-multiline-prop.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT@deadf00,4000 {\ndiff --git a/t/t4018/dts-nodes.ctx b/t/t4018/dts-nodes.ctx\ndeleted file mode 100644\nindex 3a0232d55d..0000000000\n--- a/t/t4018/dts-nodes.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-RIGHT@deadf00,4000 {\ndiff --git a/t/t4018/dts-reference b/t/t4018/dts-reference\ndeleted file mode 100644\nindex 8f0c87d863..0000000000\n--- a/t/t4018/dts-reference\n+++ /dev/null\n@@ -1,9 +0,0 @@\n-&label_1 {\n-\tTEST = <455>;\n-};\n-\n-&RIGHT {\n-\tvendor,some-property;\n-\n-\tChangeMe = <0x45-30>;\n-};\ndiff --git a/t/t4018/dts-reference.ctx b/t/t4018/dts-reference.ctx\ndeleted file mode 100644\nindex c1e13409ee..0000000000\n--- a/t/t4018/dts-reference.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-&RIGHT {\ndiff --git a/t/t4018/dts-root b/t/t4018/dts-root\ndeleted file mode 100644\nindex 4353b8220c..0000000000\n--- a/t/t4018/dts-root\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-/ { RIGHT /* Technically just supposed to be a slash and brace */\n-\t#size-cells = <1>;\n-\n-\tChangeMe = <0xffeedd00>;\n-};\ndiff --git a/t/t4018/dts-root-comment b/t/t4018/dts-root-comment\ndeleted file mode 100644\nindex 333a625c70..0000000000\n--- a/t/t4018/dts-root-comment\n+++ /dev/null\n@@ -1,8 +0,0 @@\n-/ { RIGHT /* Technically just supposed to be a slash and brace */\n-\t#size-cells = <1>;\n-\n-\t/* This comment should be ignored */\n-\n-\tsome-property = <40+2>;\n-\tChangeMe = <0xffeedd00>;\n-};\ndiff --git a/t/t4018/dts-root-comment.ctx b/t/t4018/dts-root-comment.ctx\ndeleted file mode 100644\nindex 656053dd42..0000000000\n--- a/t/t4018/dts-root-comment.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-/ { RIGHT /* Technically just supposed to be a slash and brace */\ndiff --git a/t/t4018/dts-root.ctx b/t/t4018/dts-root.ctx\ndeleted file mode 100644\nindex 656053dd42..0000000000\n--- a/t/t4018/dts-root.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-/ { RIGHT /* Technically just supposed to be a slash and brace */\ndiff --git a/t/t4018/dts.sh b/t/t4018/dts.sh\nnew file mode 100755\nindex 0000000000..304c131d86\n--- /dev/null\n+++ b/t/t4018/dts.sh\n@@ -0,0 +1,149 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'dts: labels' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+label2: RIGHT {\n+EOF_HUNK\n+/ {\n+\tlabel_1: node1@ff00 {\n+\t\tlabel2: RIGHT {\n+\t\t\tvendor,some-property;\n+\n+\t\t\tChangeMe = <0x45-30>;\n+\t\t};\n+\t};\n+};\n+EOF_TEST\n+\n+test_diff_funcname 'dts: node unitless' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT {\n+EOF_HUNK\n+/ {\n+\tlabel_1: node1 {\n+\t\tRIGHT {\n+\t\t\tprop-array = <1>, <4>;\n+\t\t\tChangeMe = <0xffeedd00>;\n+\t\t};\n+\t};\n+};\n+EOF_TEST\n+\n+test_diff_funcname 'dts: nodes' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT@deadf00,4000 {\n+EOF_HUNK\n+/ {\n+\tlabel_1: node1@ff00 {\n+\t\tRIGHT@deadf00,4000 {\n+\t\t\t#size-cells = <1>;\n+\t\t\tChangeMe = <0xffeedd00>;\n+\t\t};\n+\t};\n+};\n+EOF_TEST\n+\n+test_diff_funcname 'dts: nodes boolean prop' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT@deadf00,4000 {\n+EOF_HUNK\n+/ {\n+\tlabel_1: node1@ff00 {\n+\t\tRIGHT@deadf00,4000 {\n+\t\t\tboolean-prop1;\n+\n+\t\t\tChangeMe;\n+\t\t};\n+\t};\n+};\n+EOF_TEST\n+\n+test_diff_funcname 'dts: nodes comment1' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT@deadf00,4000 /* &a comment */ {\n+EOF_HUNK\n+/ {\n+\tlabel_1: node1@ff00 {\n+\t\tRIGHT@deadf00,4000 /* &a comment */ {\n+\t\t\t#size-cells = <1>;\n+\t\t\tChangeMe = <0xffeedd00>;\n+\t\t};\n+\t};\n+};\n+EOF_TEST\n+\n+test_diff_funcname 'dts: nodes comment2' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT@deadf00,4000 { /* a trailing comment */\n+EOF_HUNK\n+/ {\n+\tlabel_1: node1@ff00 {\n+\t\tRIGHT@deadf00,4000 { /* a trailing comment */ \n+\t\t\t#size-cells = <1>;\n+\t\t\tChangeMe = <0xffeedd00>;\n+\t\t};\n+\t};\n+};\n+EOF_TEST\n+\n+test_diff_funcname 'dts: nodes multiline prop' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT@deadf00,4000 {\n+EOF_HUNK\n+/ {\n+\tlabel_1: node1@ff00 {\n+\t\tRIGHT@deadf00,4000 {\n+\t\t\tmultilineprop = <3>,\n+\t\t\t\t\t<4>,\n+\t\t\t\t\t<5>,\n+\t\t\t\t\t<6>,\n+\t\t\t\t\t<7>;\n+\n+\t\t\tChangeMe = <0xffeedd00>;\n+\t\t};\n+\t};\n+};\n+EOF_TEST\n+\n+test_diff_funcname 'dts: reference' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+&RIGHT {\n+EOF_HUNK\n+&label_1 {\n+\tTEST = <455>;\n+};\n+\n+&RIGHT {\n+\tvendor,some-property;\n+\n+\tChangeMe = <0x45-30>;\n+};\n+EOF_TEST\n+\n+test_diff_funcname 'dts: root' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+/ { RIGHT /* Technically just supposed to be a slash and brace */\n+EOF_HUNK\n+/ { RIGHT /* Technically just supposed to be a slash and brace */\n+\t#size-cells = <1>;\n+\n+\tChangeMe = <0xffeedd00>;\n+};\n+EOF_TEST\n+\n+test_diff_funcname 'dts: root comment' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+/ { RIGHT /* Technically just supposed to be a slash and brace */\n+EOF_HUNK\n+/ { RIGHT /* Technically just supposed to be a slash and brace */\n+\t#size-cells = <1>;\n+\n+\t/* This comment should be ignored */\n+\n+\tsome-property = <40+2>;\n+\tChangeMe = <0xffeedd00>;\n+};\n+EOF_TEST\ndiff --git a/t/t4018/elixir-do-not-pick-end b/t/t4018/elixir-do-not-pick-end\ndeleted file mode 100644\nindex fae08ba7e8..0000000000\n--- a/t/t4018/elixir-do-not-pick-end\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-defmodule RIGHT do\n-end\n-#\n-#\n-# ChangeMe; do not pick up 'end' line\ndiff --git a/t/t4018/elixir-do-not-pick-end.ctx b/t/t4018/elixir-do-not-pick-end.ctx\ndeleted file mode 100644\nindex 8f28a7a689..0000000000\n--- a/t/t4018/elixir-do-not-pick-end.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-defmodule RIGHT do\ndiff --git a/t/t4018/elixir-ex-unit-test b/t/t4018/elixir-ex-unit-test\ndeleted file mode 100644\nindex 0560a2b697..0000000000\n--- a/t/t4018/elixir-ex-unit-test\n+++ /dev/null\n@@ -1,6 +0,0 @@\n-defmodule Test do\n-  test \"RIGHT\" do\n-    assert true == true\n-    assert ChangeMe\n-  end\n-end\ndiff --git a/t/t4018/elixir-ex-unit-test.ctx b/t/t4018/elixir-ex-unit-test.ctx\ndeleted file mode 100644\nindex a55e3de2cc..0000000000\n--- a/t/t4018/elixir-ex-unit-test.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-test \"RIGHT\" do\ndiff --git a/t/t4018/elixir-function b/t/t4018/elixir-function\ndeleted file mode 100644\nindex d452f495a7..0000000000\n--- a/t/t4018/elixir-function\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-def function(RIGHT, arg) do\n-  # comment\n-  # comment\n-  ChangeMe\n-end\ndiff --git a/t/t4018/elixir-function.ctx b/t/t4018/elixir-function.ctx\ndeleted file mode 100644\nindex 62aee9c8b1..0000000000\n--- a/t/t4018/elixir-function.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-def function(RIGHT, arg) do\ndiff --git a/t/t4018/elixir-macro b/t/t4018/elixir-macro\ndeleted file mode 100644\nindex 4f925e9ad4..0000000000\n--- a/t/t4018/elixir-macro\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-defmacro foo(RIGHT) do\n-  # Code\n-  # Code\n-  ChangeMe\n-end\ndiff --git a/t/t4018/elixir-macro.ctx b/t/t4018/elixir-macro.ctx\ndeleted file mode 100644\nindex fc1d3b85e8..0000000000\n--- a/t/t4018/elixir-macro.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-defmacro foo(RIGHT) do\ndiff --git a/t/t4018/elixir-module b/t/t4018/elixir-module\ndeleted file mode 100644\nindex 91a4e7aa20..0000000000\n--- a/t/t4018/elixir-module\n+++ /dev/null\n@@ -1,9 +0,0 @@\n-defmodule RIGHT do\n-  @moduledoc \"\"\"\n-  Foo bar\n-  \"\"\"\n-\n-  def ChangeMe(a) where is_map(a) do\n-    a\n-  end\n-end\ndiff --git a/t/t4018/elixir-module-func b/t/t4018/elixir-module-func\ndeleted file mode 100644\nindex c9910d0675..0000000000\n--- a/t/t4018/elixir-module-func\n+++ /dev/null\n@@ -1,8 +0,0 @@\n-defmodule Foo do\n-  def fun(RIGHT) do\n-     # Code\n-     # Code\n-     # Code\n-     ChangeMe\n-  end\n-end\ndiff --git a/t/t4018/elixir-module-func.ctx b/t/t4018/elixir-module-func.ctx\ndeleted file mode 100644\nindex 8239214386..0000000000\n--- a/t/t4018/elixir-module-func.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-def fun(RIGHT) do\ndiff --git a/t/t4018/elixir-module.ctx b/t/t4018/elixir-module.ctx\ndeleted file mode 100644\nindex 8f28a7a689..0000000000\n--- a/t/t4018/elixir-module.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-defmodule RIGHT do\ndiff --git a/t/t4018/elixir-nested-module b/t/t4018/elixir-nested-module\ndeleted file mode 100644\nindex 771ebc5c42..0000000000\n--- a/t/t4018/elixir-nested-module\n+++ /dev/null\n@@ -1,9 +0,0 @@\n-defmodule MyApp.RIGHT do\n-  @moduledoc \"\"\"\n-  Foo bar\n-  \"\"\"\n-\n-  def ChangeMe(a) where is_map(a) do\n-    a\n-  end\n-end\ndiff --git a/t/t4018/elixir-nested-module.ctx b/t/t4018/elixir-nested-module.ctx\ndeleted file mode 100644\nindex 3ffbdd18b1..0000000000\n--- a/t/t4018/elixir-nested-module.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-defmodule MyApp.RIGHT do\ndiff --git a/t/t4018/elixir-private-function b/t/t4018/elixir-private-function\ndeleted file mode 100644\nindex 1aabe33b7a..0000000000\n--- a/t/t4018/elixir-private-function\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-defp function(RIGHT, arg) do\n-  # comment\n-  # comment\n-  ChangeMe\n-end\ndiff --git a/t/t4018/elixir-private-function.ctx b/t/t4018/elixir-private-function.ctx\ndeleted file mode 100644\nindex 1c4eba44f7..0000000000\n--- a/t/t4018/elixir-private-function.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-defp function(RIGHT, arg) do\ndiff --git a/t/t4018/elixir-protocol b/t/t4018/elixir-protocol\ndeleted file mode 100644\nindex 7d9173691e..0000000000\n--- a/t/t4018/elixir-protocol\n+++ /dev/null\n@@ -1,6 +0,0 @@\n-defprotocol RIGHT do\n-  @doc \"\"\"\n-  Calculates the size (and not the length!) of a data structure\n-  \"\"\"\n-  def size(data, ChangeMe)\n-end\ndiff --git a/t/t4018/elixir-protocol-implementation b/t/t4018/elixir-protocol-implementation\ndeleted file mode 100644\nindex f9234bbfc4..0000000000\n--- a/t/t4018/elixir-protocol-implementation\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-defimpl RIGHT do\n-  # Docs\n-  # Docs\n-  def foo(ChangeMe), do: :ok\n-end\ndiff --git a/t/t4018/elixir-protocol-implementation.ctx b/t/t4018/elixir-protocol-implementation.ctx\ndeleted file mode 100644\nindex efb758aea6..0000000000\n--- a/t/t4018/elixir-protocol-implementation.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-defimpl RIGHT do\ndiff --git a/t/t4018/elixir-protocol.ctx b/t/t4018/elixir-protocol.ctx\ndeleted file mode 100644\nindex d0204e9f14..0000000000\n--- a/t/t4018/elixir-protocol.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-defprotocol RIGHT do\ndiff --git a/t/t4018/elixir.sh b/t/t4018/elixir.sh\nnew file mode 100755\nindex 0000000000..6b09df9520\n--- /dev/null\n+++ b/t/t4018/elixir.sh\n@@ -0,0 +1,127 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'elixir: do not pick end' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+defmodule RIGHT do\n+EOF_HUNK\n+defmodule RIGHT do\n+end\n+#\n+#\n+# ChangeMe; do not pick up 'end' line\n+EOF_TEST\n+\n+test_diff_funcname 'elixir: ex unit test' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+test \"RIGHT\" do\n+EOF_HUNK\n+defmodule Test do\n+  test \"RIGHT\" do\n+    assert true == true\n+    assert ChangeMe\n+  end\n+end\n+EOF_TEST\n+\n+test_diff_funcname 'elixir: function' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+def function(RIGHT, arg) do\n+EOF_HUNK\n+def function(RIGHT, arg) do\n+  # comment\n+  # comment\n+  ChangeMe\n+end\n+EOF_TEST\n+\n+test_diff_funcname 'elixir: macro' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+defmacro foo(RIGHT) do\n+EOF_HUNK\n+defmacro foo(RIGHT) do\n+  # Code\n+  # Code\n+  ChangeMe\n+end\n+EOF_TEST\n+\n+test_diff_funcname 'elixir: module' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+defmodule RIGHT do\n+EOF_HUNK\n+defmodule RIGHT do\n+  @moduledoc \"\"\"\n+  Foo bar\n+  \"\"\"\n+\n+  def ChangeMe(a) where is_map(a) do\n+    a\n+  end\n+end\n+EOF_TEST\n+\n+test_diff_funcname 'elixir: module func' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+def fun(RIGHT) do\n+EOF_HUNK\n+defmodule Foo do\n+  def fun(RIGHT) do\n+     # Code\n+     # Code\n+     # Code\n+     ChangeMe\n+  end\n+end\n+EOF_TEST\n+\n+test_diff_funcname 'elixir: nested module' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+defmodule MyApp.RIGHT do\n+EOF_HUNK\n+defmodule MyApp.RIGHT do\n+  @moduledoc \"\"\"\n+  Foo bar\n+  \"\"\"\n+\n+  def ChangeMe(a) where is_map(a) do\n+    a\n+  end\n+end\n+EOF_TEST\n+\n+test_diff_funcname 'elixir: private function' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+defp function(RIGHT, arg) do\n+EOF_HUNK\n+defp function(RIGHT, arg) do\n+  # comment\n+  # comment\n+  ChangeMe\n+end\n+EOF_TEST\n+\n+test_diff_funcname 'elixir: protocol' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+defprotocol RIGHT do\n+EOF_HUNK\n+defprotocol RIGHT do\n+  @doc \"\"\"\n+  Calculates the size (and not the length!) of a data structure\n+  \"\"\"\n+  def size(data, ChangeMe)\n+end\n+EOF_TEST\n+\n+test_diff_funcname 'elixir: protocol implementation' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+defimpl RIGHT do\n+EOF_HUNK\n+defimpl RIGHT do\n+  # Docs\n+  # Docs\n+  def foo(ChangeMe), do: :ok\n+end\n+EOF_TEST\ndiff --git a/t/t4018/fortran-block-data b/t/t4018/fortran-block-data\ndeleted file mode 100644\nindex 63d4e21d0a..0000000000\n--- a/t/t4018/fortran-block-data\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-       BLOCK DATA RIGHT\n-       \n-       COMMON /B/ C, ChangeMe\n-       DATA C, ChangeMe  / 2.0, 6.0 / \n-       END \ndiff --git a/t/t4018/fortran-block-data.ctx b/t/t4018/fortran-block-data.ctx\ndeleted file mode 100644\nindex c3db084ccc..0000000000\n--- a/t/t4018/fortran-block-data.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-BLOCK DATA RIGHT\ndiff --git a/t/t4018/fortran-comment b/t/t4018/fortran-comment\ndeleted file mode 100644\nindex 7b10d17658..0000000000\n--- a/t/t4018/fortran-comment\n+++ /dev/null\n@@ -1,13 +0,0 @@\n-      module a\n-\n-      contains\n-\n-      ! subroutine wrong\n-      subroutine RIGHT\n-      ! subroutine wrong\n-\n-      real ChangeMe\n-\n-      end subroutine RIGHT\n-\n-      end module a\ndiff --git a/t/t4018/fortran-comment-keyword b/t/t4018/fortran-comment-keyword\ndeleted file mode 100644\nindex e9206a5379..0000000000\n--- a/t/t4018/fortran-comment-keyword\n+++ /dev/null\n@@ -1,14 +0,0 @@\n-      module a\n-\n-      contains\n-\n-      subroutine RIGHT (funcA, funcB)\n-\n-      real funcA  ! grid function a\n-      real funcB  ! grid function b\n-\n-      real ChangeMe\n-\n-      end subroutine RIGHT\n-\n-      end module a\ndiff --git a/t/t4018/fortran-comment-keyword.ctx b/t/t4018/fortran-comment-keyword.ctx\ndeleted file mode 100644\nindex 0b9220b355..0000000000\n--- a/t/t4018/fortran-comment-keyword.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-subroutine RIGHT (funcA, funcB)\ndiff --git a/t/t4018/fortran-comment-legacy b/t/t4018/fortran-comment-legacy\ndeleted file mode 100644\nindex 53cd062c1e..0000000000\n--- a/t/t4018/fortran-comment-legacy\n+++ /dev/null\n@@ -1,13 +0,0 @@\n-      module a\n-\n-      contains\n-\n-C subroutine wrong\n-      subroutine RIGHT\n-C subroutine wrong\n-\n-      real ChangeMe\n-\n-      end subroutine RIGHT\n-\n-      end module a\ndiff --git a/t/t4018/fortran-comment-legacy-star b/t/t4018/fortran-comment-legacy-star\ndeleted file mode 100644\nindex 2cbcdc3d8a..0000000000\n--- a/t/t4018/fortran-comment-legacy-star\n+++ /dev/null\n@@ -1,13 +0,0 @@\n-      module a\n-\n-      contains\n-\n-* subroutine wrong\n-      subroutine RIGHT\n-* subroutine wrong\n-\n-      real ChangeMe\n-\n-      end subroutine RIGHT\n-\n-      end module a\ndiff --git a/t/t4018/fortran-comment-legacy-star.ctx b/t/t4018/fortran-comment-legacy-star.ctx\ndeleted file mode 100644\nindex 6a34203f80..0000000000\n--- a/t/t4018/fortran-comment-legacy-star.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-subroutine RIGHT\ndiff --git a/t/t4018/fortran-comment-legacy.ctx b/t/t4018/fortran-comment-legacy.ctx\ndeleted file mode 100644\nindex 6a34203f80..0000000000\n--- a/t/t4018/fortran-comment-legacy.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-subroutine RIGHT\ndiff --git a/t/t4018/fortran-comment.ctx b/t/t4018/fortran-comment.ctx\ndeleted file mode 100644\nindex 6a34203f80..0000000000\n--- a/t/t4018/fortran-comment.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-subroutine RIGHT\ndiff --git a/t/t4018/fortran-external-function b/t/t4018/fortran-external-function\ndeleted file mode 100644\nindex 5a2d85d3aa..0000000000\n--- a/t/t4018/fortran-external-function\n+++ /dev/null\n@@ -1,9 +0,0 @@\n-function RIGHT(a, b) result(c)\n-\n-integer, intent(in) :: ChangeMe\n-integer, intent(in) :: b\n-integer, intent(out) :: c\n-\n-c = a+b\n-\n-end function RIGHT\ndiff --git a/t/t4018/fortran-external-function.ctx b/t/t4018/fortran-external-function.ctx\ndeleted file mode 100644\nindex 56ec4d8eca..0000000000\n--- a/t/t4018/fortran-external-function.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-function RIGHT(a, b) result(c)\ndiff --git a/t/t4018/fortran-external-subroutine b/t/t4018/fortran-external-subroutine\ndeleted file mode 100644\nindex 4ce85fea13..0000000000\n--- a/t/t4018/fortran-external-subroutine\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-subroutine RIGHT\n-\n-real ChangeMe\n-\n-end subroutine RIGHT\ndiff --git a/t/t4018/fortran-external-subroutine.ctx b/t/t4018/fortran-external-subroutine.ctx\ndeleted file mode 100644\nindex 6a34203f80..0000000000\n--- a/t/t4018/fortran-external-subroutine.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-subroutine RIGHT\ndiff --git a/t/t4018/fortran-module b/t/t4018/fortran-module\ndeleted file mode 100644\nindex c4b737dac3..0000000000\n--- a/t/t4018/fortran-module\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-module RIGHT\n-\n-use ChangeMe\n-\n-end module RIGHT\ndiff --git a/t/t4018/fortran-module-procedure b/t/t4018/fortran-module-procedure\ndeleted file mode 100644\nindex 1ce6d854c2..0000000000\n--- a/t/t4018/fortran-module-procedure\n+++ /dev/null\n@@ -1,13 +0,0 @@\n- module RIGHT\n-\n-   implicit none\n-   private\n-\n-   interface letters  ! generic interface\n-      module procedure aaaa, &\n-                       bbbb, &\n-                       ChangeMe, &\n-                       dddd\n-   end interface\n-   \n-end module RIGHT\ndiff --git a/t/t4018/fortran-module-procedure.ctx b/t/t4018/fortran-module-procedure.ctx\ndeleted file mode 100644\nindex 4f5ff2e4b8..0000000000\n--- a/t/t4018/fortran-module-procedure.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-module RIGHT\ndiff --git a/t/t4018/fortran-module.ctx b/t/t4018/fortran-module.ctx\ndeleted file mode 100644\nindex 4f5ff2e4b8..0000000000\n--- a/t/t4018/fortran-module.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-module RIGHT\ndiff --git a/t/t4018/fortran-program b/t/t4018/fortran-program\ndeleted file mode 100644\nindex 4616895e4b..0000000000\n--- a/t/t4018/fortran-program\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-program RIGHT\n-\n-call ChangeMe\n-\n-end program RIGHT\ndiff --git a/t/t4018/fortran-program.ctx b/t/t4018/fortran-program.ctx\ndeleted file mode 100644\nindex c4e844df30..0000000000\n--- a/t/t4018/fortran-program.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-program RIGHT\ndiff --git a/t/t4018/fortran.sh b/t/t4018/fortran.sh\nnew file mode 100755\nindex 0000000000..7b0c6789d3\n--- /dev/null\n+++ b/t/t4018/fortran.sh\n@@ -0,0 +1,159 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'fortran: block data' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+BLOCK DATA RIGHT\n+EOF_HUNK\n+       BLOCK DATA RIGHT\n+       \n+       COMMON /B/ C, ChangeMe\n+       DATA C, ChangeMe  / 2.0, 6.0 / \n+       END \n+EOF_TEST\n+\n+test_diff_funcname 'fortran: comment' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+subroutine RIGHT\n+EOF_HUNK\n+      module a\n+\n+      contains\n+\n+      ! subroutine wrong\n+      subroutine RIGHT\n+      ! subroutine wrong\n+\n+      real ChangeMe\n+\n+      end subroutine RIGHT\n+\n+      end module a\n+EOF_TEST\n+\n+test_diff_funcname 'fortran: comment keyword' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+subroutine RIGHT (funcA, funcB)\n+EOF_HUNK\n+      module a\n+\n+      contains\n+\n+      subroutine RIGHT (funcA, funcB)\n+\n+      real funcA  ! grid function a\n+      real funcB  ! grid function b\n+\n+      real ChangeMe\n+\n+      end subroutine RIGHT\n+\n+      end module a\n+EOF_TEST\n+\n+test_diff_funcname 'fortran: comment legacy' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+subroutine RIGHT\n+EOF_HUNK\n+      module a\n+\n+      contains\n+\n+C subroutine wrong\n+      subroutine RIGHT\n+C subroutine wrong\n+\n+      real ChangeMe\n+\n+      end subroutine RIGHT\n+\n+      end module a\n+EOF_TEST\n+\n+test_diff_funcname 'fortran: comment legacy star' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+subroutine RIGHT\n+EOF_HUNK\n+      module a\n+\n+      contains\n+\n+* subroutine wrong\n+      subroutine RIGHT\n+* subroutine wrong\n+\n+      real ChangeMe\n+\n+      end subroutine RIGHT\n+\n+      end module a\n+EOF_TEST\n+\n+test_diff_funcname 'fortran: external function' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+function RIGHT(a, b) result(c)\n+EOF_HUNK\n+function RIGHT(a, b) result(c)\n+\n+integer, intent(in) :: ChangeMe\n+integer, intent(in) :: b\n+integer, intent(out) :: c\n+\n+c = a+b\n+\n+end function RIGHT\n+EOF_TEST\n+\n+test_diff_funcname 'fortran: external subroutine' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+subroutine RIGHT\n+EOF_HUNK\n+subroutine RIGHT\n+\n+real ChangeMe\n+\n+end subroutine RIGHT\n+EOF_TEST\n+\n+test_diff_funcname 'fortran: module' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+module RIGHT\n+EOF_HUNK\n+module RIGHT\n+\n+use ChangeMe\n+\n+end module RIGHT\n+EOF_TEST\n+\n+test_diff_funcname 'fortran: module procedure' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+module RIGHT\n+EOF_HUNK\n+ module RIGHT\n+\n+   implicit none\n+   private\n+\n+   interface letters  ! generic interface\n+      module procedure aaaa, &\n+                       bbbb, &\n+                       ChangeMe, &\n+                       dddd\n+   end interface\n+   \n+end module RIGHT\n+EOF_TEST\n+\n+test_diff_funcname 'fortran: program' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+program RIGHT\n+EOF_HUNK\n+program RIGHT\n+\n+call ChangeMe\n+\n+end program RIGHT\n+EOF_TEST\ndiff --git a/t/t4018/fountain-scene b/t/t4018/fountain-scene\ndeleted file mode 100644\nindex 6b3257d680..0000000000\n--- a/t/t4018/fountain-scene\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-EXT. STREET RIGHT OUTSIDE - DAY\n-\n-CHARACTER\n-You didn't say the magic phrase, \"ChangeMe\".\ndiff --git a/t/t4018/fountain-scene.ctx b/t/t4018/fountain-scene.ctx\ndeleted file mode 100644\nindex bf10171418..0000000000\n--- a/t/t4018/fountain-scene.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-EXT. STREET RIGHT OUTSIDE - DAY\ndiff --git a/t/t4018/fountain.sh b/t/t4018/fountain.sh\nnew file mode 100755\nindex 0000000000..02b44d6a3f\n--- /dev/null\n+++ b/t/t4018/fountain.sh\n@@ -0,0 +1,14 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'fountain: scene' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+EXT. STREET RIGHT OUTSIDE - DAY\n+EOF_HUNK\n+EXT. STREET RIGHT OUTSIDE - DAY\n+\n+CHARACTER\n+You didn't say the magic phrase, \"ChangeMe\".\n+EOF_TEST\ndiff --git a/t/t4018/golang-complex-function b/t/t4018/golang-complex-function\ndeleted file mode 100644\nindex e057dcefed..0000000000\n--- a/t/t4018/golang-complex-function\n+++ /dev/null\n@@ -1,8 +0,0 @@\n-type Test struct {\n-\ta Type\n-}\n-\n-func (t *Test) RIGHT(a Type) (Type, error) {\n-\tt.a = a\n-\treturn ChangeMe, nil\n-}\ndiff --git a/t/t4018/golang-complex-function.ctx b/t/t4018/golang-complex-function.ctx\ndeleted file mode 100644\nindex 8e8d5582ff..0000000000\n--- a/t/t4018/golang-complex-function.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-func (t *Test) RIGHT(a Type) (Type, error) {\ndiff --git a/t/t4018/golang-func b/t/t4018/golang-func\ndeleted file mode 100644\nindex 8e9c9ac7c3..0000000000\n--- a/t/t4018/golang-func\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-func RIGHT() {\n-\ta := 5\n-\tb := ChangeMe\n-}\ndiff --git a/t/t4018/golang-func.ctx b/t/t4018/golang-func.ctx\ndeleted file mode 100644\nindex 88bc823813..0000000000\n--- a/t/t4018/golang-func.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-func RIGHT() {\ndiff --git a/t/t4018/golang-interface b/t/t4018/golang-interface\ndeleted file mode 100644\nindex 553bedec96..0000000000\n--- a/t/t4018/golang-interface\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-type RIGHT interface {\n-\ta() Type\n-\tb() ChangeMe\n-}\ndiff --git a/t/t4018/golang-interface.ctx b/t/t4018/golang-interface.ctx\ndeleted file mode 100644\nindex 2d07f5a383..0000000000\n--- a/t/t4018/golang-interface.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-type RIGHT interface {\ndiff --git a/t/t4018/golang-long-func b/t/t4018/golang-long-func\ndeleted file mode 100644\nindex ac3a77b5c4..0000000000\n--- a/t/t4018/golang-long-func\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-func RIGHT(aVeryVeryVeryLongVariableName AVeryVeryVeryLongType,\n-\tanotherLongVariableName AnotherLongType) {\n-\ta := 5\n-\tb := ChangeMe\n-}\ndiff --git a/t/t4018/golang-long-func.ctx b/t/t4018/golang-long-func.ctx\ndeleted file mode 100644\nindex 25635e712e..0000000000\n--- a/t/t4018/golang-long-func.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-func RIGHT(aVeryVeryVeryLongVariableName AVeryVeryVeryLongType,\ndiff --git a/t/t4018/golang-struct b/t/t4018/golang-struct\ndeleted file mode 100644\nindex 5deda77fee..0000000000\n--- a/t/t4018/golang-struct\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-type RIGHT struct {\n-\ta Type\n-\tb ChangeMe\n-}\ndiff --git a/t/t4018/golang-struct.ctx b/t/t4018/golang-struct.ctx\ndeleted file mode 100644\nindex 8a1240699d..0000000000\n--- a/t/t4018/golang-struct.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-type RIGHT struct {\ndiff --git a/t/t4018/golang.sh b/t/t4018/golang.sh\nnew file mode 100755\nindex 0000000000..bf22f58c12\n--- /dev/null\n+++ b/t/t4018/golang.sh\n@@ -0,0 +1,59 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'golang: complex function' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+func (t *Test) RIGHT(a Type) (Type, error) {\n+EOF_HUNK\n+type Test struct {\n+\ta Type\n+}\n+\n+func (t *Test) RIGHT(a Type) (Type, error) {\n+\tt.a = a\n+\treturn ChangeMe, nil\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'golang: func' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+func RIGHT() {\n+EOF_HUNK\n+func RIGHT() {\n+\ta := 5\n+\tb := ChangeMe\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'golang: interface' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+type RIGHT interface {\n+EOF_HUNK\n+type RIGHT interface {\n+\ta() Type\n+\tb() ChangeMe\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'golang: long func' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+func RIGHT(aVeryVeryVeryLongVariableName AVeryVeryVeryLongType,\n+EOF_HUNK\n+func RIGHT(aVeryVeryVeryLongVariableName AVeryVeryVeryLongType,\n+\tanotherLongVariableName AnotherLongType) {\n+\ta := 5\n+\tb := ChangeMe\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'golang: struct' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+type RIGHT struct {\n+EOF_HUNK\n+type RIGHT struct {\n+\ta Type\n+\tb ChangeMe\n+}\n+EOF_TEST\ndiff --git a/t/t4018/java-class-member-function b/t/t4018/java-class-member-function\ndeleted file mode 100644\nindex 298bc7a71b..0000000000\n--- a/t/t4018/java-class-member-function\n+++ /dev/null\n@@ -1,8 +0,0 @@\n-public class Beer\n-{\n-\tint special;\n-\tpublic static void main(String RIGHT[])\n-\t{\n-\t\tSystem.out.print(\"ChangeMe\");\n-\t}\n-}\ndiff --git a/t/t4018/java-class-member-function.ctx b/t/t4018/java-class-member-function.ctx\ndeleted file mode 100644\nindex 2125474b68..0000000000\n--- a/t/t4018/java-class-member-function.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-public static void main(String RIGHT[])\ndiff --git a/t/t4018/java.sh b/t/t4018/java.sh\nnew file mode 100755\nindex 0000000000..c89cf2f9d8\n--- /dev/null\n+++ b/t/t4018/java.sh\n@@ -0,0 +1,18 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'java: class member function' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+public static void main(String RIGHT[])\n+EOF_HUNK\n+public class Beer\n+{\n+\tint special;\n+\tpublic static void main(String RIGHT[])\n+\t{\n+\t\tSystem.out.print(\"ChangeMe\");\n+\t}\n+}\n+EOF_TEST\ndiff --git a/t/t4018/markdown-heading-indented b/t/t4018/markdown-heading-indented\ndeleted file mode 100644\nindex 1991c2bd45..0000000000\n--- a/t/t4018/markdown-heading-indented\n+++ /dev/null\n@@ -1,6 +0,0 @@\n-Indented headings are allowed, as long as the indent is no more than 3 spaces.\n-\n-   ### RIGHT\n-\n-- something\n-- ChangeMe\ndiff --git a/t/t4018/markdown-heading-indented.ctx b/t/t4018/markdown-heading-indented.ctx\ndeleted file mode 100644\nindex 5938336743..0000000000\n--- a/t/t4018/markdown-heading-indented.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-   ### RIGHT\ndiff --git a/t/t4018/markdown-heading-non-headings b/t/t4018/markdown-heading-non-headings\ndeleted file mode 100644\nindex c479c1a3f1..0000000000\n--- a/t/t4018/markdown-heading-non-headings\n+++ /dev/null\n@@ -1,17 +0,0 @@\n-Headings can be right next to other lines of the file:\n-# RIGHT\n-Indents of four or more spaces make a code block:\n-\n-    # code comment, not heading\n-\n-If there's no space after the final hash, it's not a heading:\n-\n-#hashtag\n-\n-Sequences of more than 6 hashes don't make a heading:\n-\n-####### over-enthusiastic heading\n-\n-So the detected heading should be right up at the start of this file.\n-\n-ChangeMe\ndiff --git a/t/t4018/markdown-heading-non-headings.ctx b/t/t4018/markdown-heading-non-headings.ctx\ndeleted file mode 100644\nindex 7e2165be6e..0000000000\n--- a/t/t4018/markdown-heading-non-headings.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-# RIGHT\ndiff --git a/t/t4018/markdown.sh b/t/t4018/markdown.sh\nnew file mode 100755\nindex 0000000000..3e1c79b139\n--- /dev/null\n+++ b/t/t4018/markdown.sh\n@@ -0,0 +1,39 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'markdown: heading indented' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+   ### RIGHT\n+EOF_HUNK\n+Indented headings are allowed, as long as the indent is no more than 3 spaces.\n+\n+   ### RIGHT\n+\n+- something\n+- ChangeMe\n+EOF_TEST\n+\n+test_diff_funcname 'markdown: heading non headings' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+# RIGHT\n+EOF_HUNK\n+Headings can be right next to other lines of the file:\n+# RIGHT\n+Indents of four or more spaces make a code block:\n+\n+    # code comment, not heading\n+\n+If there's no space after the final hash, it's not a heading:\n+\n+#hashtag\n+\n+Sequences of more than 6 hashes don't make a heading:\n+\n+####### over-enthusiastic heading\n+\n+So the detected heading should be right up at the start of this file.\n+\n+ChangeMe\n+EOF_TEST\ndiff --git a/t/t4018/matlab-class-definition b/t/t4018/matlab-class-definition\ndeleted file mode 100644\nindex 84daedfb4e..0000000000\n--- a/t/t4018/matlab-class-definition\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-classdef RIGHT\n-    properties\n-        ChangeMe\n-    end\n-end\ndiff --git a/t/t4018/matlab-class-definition.ctx b/t/t4018/matlab-class-definition.ctx\ndeleted file mode 100644\nindex 5dd5b45628..0000000000\n--- a/t/t4018/matlab-class-definition.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-classdef RIGHT\ndiff --git a/t/t4018/matlab-function b/t/t4018/matlab-function\ndeleted file mode 100644\nindex 897a9b13ff..0000000000\n--- a/t/t4018/matlab-function\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-function y = RIGHT()\n-x = 5;\n-y = ChangeMe + x;\n-end\ndiff --git a/t/t4018/matlab-function.ctx b/t/t4018/matlab-function.ctx\ndeleted file mode 100644\nindex 72d2350b13..0000000000\n--- a/t/t4018/matlab-function.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-function y = RIGHT()\ndiff --git a/t/t4018/matlab-octave-section-1 b/t/t4018/matlab-octave-section-1\ndeleted file mode 100644\nindex 3bb6c4670e..0000000000\n--- a/t/t4018/matlab-octave-section-1\n+++ /dev/null\n@@ -1,3 +0,0 @@\n-%%% RIGHT section\n-# this is octave script\n-ChangeMe = 1;\ndiff --git a/t/t4018/matlab-octave-section-1.ctx b/t/t4018/matlab-octave-section-1.ctx\ndeleted file mode 100644\nindex ca9b349f94..0000000000\n--- a/t/t4018/matlab-octave-section-1.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-%%% RIGHT section\ndiff --git a/t/t4018/matlab-octave-section-2 b/t/t4018/matlab-octave-section-2\ndeleted file mode 100644\nindex ab2980f7f2..0000000000\n--- a/t/t4018/matlab-octave-section-2\n+++ /dev/null\n@@ -1,3 +0,0 @@\n-## RIGHT section\n-# this is octave script\n-ChangeMe = 1;\ndiff --git a/t/t4018/matlab-octave-section-2.ctx b/t/t4018/matlab-octave-section-2.ctx\ndeleted file mode 100644\nindex 5cbb77faf5..0000000000\n--- a/t/t4018/matlab-octave-section-2.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-## RIGHT section\ndiff --git a/t/t4018/matlab-section b/t/t4018/matlab-section\ndeleted file mode 100644\nindex 5ea59a5de0..0000000000\n--- a/t/t4018/matlab-section\n+++ /dev/null\n@@ -1,3 +0,0 @@\n-%% RIGHT section\n-% this is understood by both matlab and octave\n-ChangeMe = 1;\ndiff --git a/t/t4018/matlab-section.ctx b/t/t4018/matlab-section.ctx\ndeleted file mode 100644\nindex e83fee6f4d..0000000000\n--- a/t/t4018/matlab-section.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-%% RIGHT section\ndiff --git a/t/t4018/matlab.sh b/t/t4018/matlab.sh\nnew file mode 100755\nindex 0000000000..f62289148e\n--- /dev/null\n+++ b/t/t4018/matlab.sh\n@@ -0,0 +1,52 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'matlab: class definition' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+classdef RIGHT\n+EOF_HUNK\n+classdef RIGHT\n+    properties\n+        ChangeMe\n+    end\n+end\n+EOF_TEST\n+\n+test_diff_funcname 'matlab: function' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+function y = RIGHT()\n+EOF_HUNK\n+function y = RIGHT()\n+x = 5;\n+y = ChangeMe + x;\n+end\n+EOF_TEST\n+\n+test_diff_funcname 'matlab: octave section 1' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+%%% RIGHT section\n+EOF_HUNK\n+%%% RIGHT section\n+# this is octave script\n+ChangeMe = 1;\n+EOF_TEST\n+\n+test_diff_funcname 'matlab: octave section 2' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+## RIGHT section\n+EOF_HUNK\n+## RIGHT section\n+# this is octave script\n+ChangeMe = 1;\n+EOF_TEST\n+\n+test_diff_funcname 'matlab: section' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+%% RIGHT section\n+EOF_HUNK\n+%% RIGHT section\n+% this is understood by both matlab and octave\n+ChangeMe = 1;\n+EOF_TEST\ndiff --git a/t/t4018/perl-skip-end-of-heredoc b/t/t4018/perl-skip-end-of-heredoc\ndeleted file mode 100644\nindex c22d39b256..0000000000\n--- a/t/t4018/perl-skip-end-of-heredoc\n+++ /dev/null\n@@ -1,8 +0,0 @@\n-sub RIGHTwithheredocument {\n-\tprint <<\"EOF\"\n-decoy here-doc\n-EOF\n-\t# some lines of context\n-\t# to pad it out\n-\tprint \"ChangeMe\\n\";\n-}\ndiff --git a/t/t4018/perl-skip-end-of-heredoc.ctx b/t/t4018/perl-skip-end-of-heredoc.ctx\ndeleted file mode 100644\nindex c15f4b78bd..0000000000\n--- a/t/t4018/perl-skip-end-of-heredoc.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-sub RIGHTwithheredocument {\ndiff --git a/t/t4018/perl-skip-forward-decl b/t/t4018/perl-skip-forward-decl\ndeleted file mode 100644\nindex a98cb8bdad..0000000000\n--- a/t/t4018/perl-skip-forward-decl\n+++ /dev/null\n@@ -1,10 +0,0 @@\n-package RIGHT;\n-\n-use strict;\n-use warnings;\n-use parent qw(Exporter);\n-our @EXPORT_OK = qw(round finalround);\n-\n-sub other; # forward declaration\n-\n-# ChangeMe\ndiff --git a/t/t4018/perl-skip-forward-decl.ctx b/t/t4018/perl-skip-forward-decl.ctx\ndeleted file mode 100644\nindex e0c51599ad..0000000000\n--- a/t/t4018/perl-skip-forward-decl.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-package RIGHT;\ndiff --git a/t/t4018/perl-skip-sub-in-pod b/t/t4018/perl-skip-sub-in-pod\ndeleted file mode 100644\nindex e39f02462e..0000000000\n--- a/t/t4018/perl-skip-sub-in-pod\n+++ /dev/null\n@@ -1,18 +0,0 @@\n-=head1 NAME\n-\n-Beer - subroutine to output fragment of a drinking song\n-\n-=head1 SYNOPSIS_RIGHT\n-\n-\tuse Beer qw(round finalround);\n-\n-\tsub song {\n-\t\tfor (my $i = 99; $i > 0; $i--) {\n-\t\t\tround $i;\n-\t\t}\n-\t\tfinalround;\n-\t}\n-\n-\tChangeMe;\n-\n-=cut\ndiff --git a/t/t4018/perl-skip-sub-in-pod.ctx b/t/t4018/perl-skip-sub-in-pod.ctx\ndeleted file mode 100644\nindex abddd76655..0000000000\n--- a/t/t4018/perl-skip-sub-in-pod.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-=head1 SYNOPSIS_RIGHT\ndiff --git a/t/t4018/perl-sub-definition b/t/t4018/perl-sub-definition\ndeleted file mode 100644\nindex a507d1f645..0000000000\n--- a/t/t4018/perl-sub-definition\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-sub RIGHT {\n-\tmy ($n) = @_;\n-\tprint \"ChangeMe\";\n-}\ndiff --git a/t/t4018/perl-sub-definition-kr-brace b/t/t4018/perl-sub-definition-kr-brace\ndeleted file mode 100644\nindex 330b3df114..0000000000\n--- a/t/t4018/perl-sub-definition-kr-brace\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-sub RIGHT\n-{\n-\tprint \"ChangeMe\\n\";\n-}\ndiff --git a/t/t4018/perl-sub-definition-kr-brace.ctx b/t/t4018/perl-sub-definition-kr-brace.ctx\ndeleted file mode 100644\nindex 7e5aee5cde..0000000000\n--- a/t/t4018/perl-sub-definition-kr-brace.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-sub RIGHT\ndiff --git a/t/t4018/perl-sub-definition.ctx b/t/t4018/perl-sub-definition.ctx\ndeleted file mode 100644\nindex d49a63598e..0000000000\n--- a/t/t4018/perl-sub-definition.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-sub RIGHT {\ndiff --git a/t/t4018/perl.sh b/t/t4018/perl.sh\nnew file mode 100755\nindex 0000000000..ac8fff7417\n--- /dev/null\n+++ b/t/t4018/perl.sh\n@@ -0,0 +1,78 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'perl: skip end of heredoc' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+sub RIGHTwithheredocument {\n+EOF_HUNK\n+sub RIGHTwithheredocument {\n+\tprint <<\"EOF\"\n+decoy here-doc\n+EOF\n+\t# some lines of context\n+\t# to pad it out\n+\tprint \"ChangeMe\\n\";\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'perl: skip forward decl' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+package RIGHT;\n+EOF_HUNK\n+package RIGHT;\n+\n+use strict;\n+use warnings;\n+use parent qw(Exporter);\n+our @EXPORT_OK = qw(round finalround);\n+\n+sub other; # forward declaration\n+\n+# ChangeMe\n+EOF_TEST\n+\n+test_diff_funcname 'perl: skip sub in pod' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+=head1 SYNOPSIS_RIGHT\n+EOF_HUNK\n+=head1 NAME\n+\n+Beer - subroutine to output fragment of a drinking song\n+\n+=head1 SYNOPSIS_RIGHT\n+\n+\tuse Beer qw(round finalround);\n+\n+\tsub song {\n+\t\tfor (my $i = 99; $i > 0; $i--) {\n+\t\t\tround $i;\n+\t\t}\n+\t\tfinalround;\n+\t}\n+\n+\tChangeMe;\n+\n+=cut\n+EOF_TEST\n+\n+test_diff_funcname 'perl: sub definition' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+sub RIGHT {\n+EOF_HUNK\n+sub RIGHT {\n+\tmy ($n) = @_;\n+\tprint \"ChangeMe\";\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'perl: sub definition kr brace' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+sub RIGHT\n+EOF_HUNK\n+sub RIGHT\n+{\n+\tprint \"ChangeMe\\n\";\n+}\n+EOF_TEST\ndiff --git a/t/t4018/php-abstract-class b/t/t4018/php-abstract-class\ndeleted file mode 100644\nindex 5213e12494..0000000000\n--- a/t/t4018/php-abstract-class\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-abstract class RIGHT\n-{\n-    const FOO = 'ChangeMe';\n-}\ndiff --git a/t/t4018/php-abstract-class.ctx b/t/t4018/php-abstract-class.ctx\ndeleted file mode 100644\nindex f572d2129b..0000000000\n--- a/t/t4018/php-abstract-class.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-abstract class RIGHT\ndiff --git a/t/t4018/php-abstract-method b/t/t4018/php-abstract-method\ndeleted file mode 100644\nindex ce215df75a..0000000000\n--- a/t/t4018/php-abstract-method\n+++ /dev/null\n@@ -1,7 +0,0 @@\n-abstract class Klass\n-{\n-    abstract public function RIGHT(): ?string\n-    {\n-        return 'ChangeMe';\n-    }\n-}\ndiff --git a/t/t4018/php-abstract-method.ctx b/t/t4018/php-abstract-method.ctx\ndeleted file mode 100644\nindex 14cb6df42e..0000000000\n--- a/t/t4018/php-abstract-method.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-abstract public function RIGHT(): ?string\ndiff --git a/t/t4018/php-class b/t/t4018/php-class\ndeleted file mode 100644\nindex 7785b6303c..0000000000\n--- a/t/t4018/php-class\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-class RIGHT\n-{\n-    const FOO = 'ChangeMe';\n-}\ndiff --git a/t/t4018/php-class.ctx b/t/t4018/php-class.ctx\ndeleted file mode 100644\nindex 54bff816d6..0000000000\n--- a/t/t4018/php-class.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-class RIGHT\ndiff --git a/t/t4018/php-final-class b/t/t4018/php-final-class\ndeleted file mode 100644\nindex 69f5710552..0000000000\n--- a/t/t4018/php-final-class\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-final class RIGHT\n-{\n-    const FOO = 'ChangeMe';\n-}\ndiff --git a/t/t4018/php-final-class.ctx b/t/t4018/php-final-class.ctx\ndeleted file mode 100644\nindex 4d59fb749b..0000000000\n--- a/t/t4018/php-final-class.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-final class RIGHT\ndiff --git a/t/t4018/php-final-method b/t/t4018/php-final-method\ndeleted file mode 100644\nindex 537fb8ad9a..0000000000\n--- a/t/t4018/php-final-method\n+++ /dev/null\n@@ -1,7 +0,0 @@\n-class Klass\n-{\n-    final public function RIGHT(): string\n-    {\n-        return 'ChangeMe';\n-    }\n-}\ndiff --git a/t/t4018/php-final-method.ctx b/t/t4018/php-final-method.ctx\ndeleted file mode 100644\nindex b7da8f8082..0000000000\n--- a/t/t4018/php-final-method.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-final public function RIGHT(): string\ndiff --git a/t/t4018/php-function b/t/t4018/php-function\ndeleted file mode 100644\nindex 35717c51c3..0000000000\n--- a/t/t4018/php-function\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-function RIGHT()\n-{\n-    return 'ChangeMe';\n-}\ndiff --git a/t/t4018/php-function.ctx b/t/t4018/php-function.ctx\ndeleted file mode 100644\nindex c5f3e55302..0000000000\n--- a/t/t4018/php-function.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-function RIGHT()\ndiff --git a/t/t4018/php-interface b/t/t4018/php-interface\ndeleted file mode 100644\nindex 86b49ad5d9..0000000000\n--- a/t/t4018/php-interface\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-interface RIGHT\n-{\n-    public function foo($ChangeMe);\n-}\ndiff --git a/t/t4018/php-interface.ctx b/t/t4018/php-interface.ctx\ndeleted file mode 100644\nindex a45fa0532a..0000000000\n--- a/t/t4018/php-interface.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-interface RIGHT\ndiff --git a/t/t4018/php-method b/t/t4018/php-method\ndeleted file mode 100644\nindex 03af1a6d9d..0000000000\n--- a/t/t4018/php-method\n+++ /dev/null\n@@ -1,7 +0,0 @@\n-class Klass\n-{\n-    public static function RIGHT()\n-    {\n-        return 'ChangeMe';\n-    }\n-}\ndiff --git a/t/t4018/php-method.ctx b/t/t4018/php-method.ctx\ndeleted file mode 100644\nindex eb1659ff9f..0000000000\n--- a/t/t4018/php-method.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-public static function RIGHT()\ndiff --git a/t/t4018/php-trait b/t/t4018/php-trait\ndeleted file mode 100644\nindex 65b8c82a61..0000000000\n--- a/t/t4018/php-trait\n+++ /dev/null\n@@ -1,7 +0,0 @@\n-trait RIGHT\n-{\n-    public function foo($ChangeMe)\n-    {\n-        return 'foo';\n-    }\n-}\ndiff --git a/t/t4018/php-trait.ctx b/t/t4018/php-trait.ctx\ndeleted file mode 100644\nindex 57aa4c6267..0000000000\n--- a/t/t4018/php-trait.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-trait RIGHT\ndiff --git a/t/t4018/php.sh b/t/t4018/php.sh\nnew file mode 100755\nindex 0000000000..e0ccb2277b\n--- /dev/null\n+++ b/t/t4018/php.sh\n@@ -0,0 +1,106 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'php: abstract class' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+abstract class RIGHT\n+EOF_HUNK\n+abstract class RIGHT\n+{\n+    const FOO = 'ChangeMe';\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'php: abstract method' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+abstract public function RIGHT(): ?string\n+EOF_HUNK\n+abstract class Klass\n+{\n+    abstract public function RIGHT(): ?string\n+    {\n+        return 'ChangeMe';\n+    }\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'php: class' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+class RIGHT\n+EOF_HUNK\n+class RIGHT\n+{\n+    const FOO = 'ChangeMe';\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'php: final class' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+final class RIGHT\n+EOF_HUNK\n+final class RIGHT\n+{\n+    const FOO = 'ChangeMe';\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'php: final method' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+final public function RIGHT(): string\n+EOF_HUNK\n+class Klass\n+{\n+    final public function RIGHT(): string\n+    {\n+        return 'ChangeMe';\n+    }\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'php: function' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+function RIGHT()\n+EOF_HUNK\n+function RIGHT()\n+{\n+    return 'ChangeMe';\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'php: interface' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+interface RIGHT\n+EOF_HUNK\n+interface RIGHT\n+{\n+    public function foo($ChangeMe);\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'php: method' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+public static function RIGHT()\n+EOF_HUNK\n+class Klass\n+{\n+    public static function RIGHT()\n+    {\n+        return 'ChangeMe';\n+    }\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'php: trait' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+trait RIGHT\n+EOF_HUNK\n+trait RIGHT\n+{\n+    public function foo($ChangeMe)\n+    {\n+        return 'foo';\n+    }\n+}\n+EOF_TEST\ndiff --git a/t/t4018/python-async-def b/t/t4018/python-async-def\ndeleted file mode 100644\nindex 87640e03d2..0000000000\n--- a/t/t4018/python-async-def\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-async def RIGHT(pi: int = 3.14):\n-    while True:\n-        break\n-    return ChangeMe()\ndiff --git a/t/t4018/python-async-def.ctx b/t/t4018/python-async-def.ctx\ndeleted file mode 100644\nindex 468c548bbe..0000000000\n--- a/t/t4018/python-async-def.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-async def RIGHT(pi: int = 3.14):\ndiff --git a/t/t4018/python-class b/t/t4018/python-class\ndeleted file mode 100644\nindex ba9e741430..0000000000\n--- a/t/t4018/python-class\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-class RIGHT(int, str):\n-    # comment\n-    # another comment\n-    # ChangeMe\ndiff --git a/t/t4018/python-class.ctx b/t/t4018/python-class.ctx\ndeleted file mode 100644\nindex a40b755e29..0000000000\n--- a/t/t4018/python-class.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-class RIGHT(int, str):\ndiff --git a/t/t4018/python-def b/t/t4018/python-def\ndeleted file mode 100644\nindex e50b31b0ad..0000000000\n--- a/t/t4018/python-def\n+++ /dev/null\n@@ -1,4 +0,0 @@\n-def RIGHT(pi: int = 3.14):\n-    while True:\n-        break\n-    return ChangeMe()\ndiff --git a/t/t4018/python-def.ctx b/t/t4018/python-def.ctx\ndeleted file mode 100644\nindex a1a9cbad63..0000000000\n--- a/t/t4018/python-def.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-def RIGHT(pi: int = 3.14):\ndiff --git a/t/t4018/python-indented-async-def b/t/t4018/python-indented-async-def\ndeleted file mode 100644\nindex f5d03258af..0000000000\n--- a/t/t4018/python-indented-async-def\n+++ /dev/null\n@@ -1,7 +0,0 @@\n-class Foo:\n-    async def RIGHT(self, x: int):\n-        return [\n-            1,\n-            2,\n-            ChangeMe,\n-        ]\ndiff --git a/t/t4018/python-indented-async-def.ctx b/t/t4018/python-indented-async-def.ctx\ndeleted file mode 100644\nindex d393620a1e..0000000000\n--- a/t/t4018/python-indented-async-def.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-async def RIGHT(self, x: int):\ndiff --git a/t/t4018/python-indented-class b/t/t4018/python-indented-class\ndeleted file mode 100644\nindex 19b4f35c4c..0000000000\n--- a/t/t4018/python-indented-class\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-if TYPE_CHECKING:\n-    class RIGHT:\n-        # comment\n-        # another comment\n-        # ChangeMe\ndiff --git a/t/t4018/python-indented-class.ctx b/t/t4018/python-indented-class.ctx\ndeleted file mode 100644\nindex 0881c84dba..0000000000\n--- a/t/t4018/python-indented-class.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-class RIGHT:\ndiff --git a/t/t4018/python-indented-def b/t/t4018/python-indented-def\ndeleted file mode 100644\nindex 208fbadd2b..0000000000\n--- a/t/t4018/python-indented-def\n+++ /dev/null\n@@ -1,7 +0,0 @@\n-class Foo:\n-    def RIGHT(self, x: int):\n-        return [\n-            1,\n-            2,\n-            ChangeMe,\n-        ]\ndiff --git a/t/t4018/python-indented-def.ctx b/t/t4018/python-indented-def.ctx\ndeleted file mode 100644\nindex 6e5a44b391..0000000000\n--- a/t/t4018/python-indented-def.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-def RIGHT(self, x: int):\ndiff --git a/t/t4018/python.sh b/t/t4018/python.sh\nnew file mode 100755\nindex 0000000000..ecb5736d57\n--- /dev/null\n+++ b/t/t4018/python.sh\n@@ -0,0 +1,71 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'python: async def' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+async def RIGHT(pi: int = 3.14):\n+EOF_HUNK\n+async def RIGHT(pi: int = 3.14):\n+    while True:\n+        break\n+    return ChangeMe()\n+EOF_TEST\n+\n+test_diff_funcname 'python: class' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+class RIGHT(int, str):\n+EOF_HUNK\n+class RIGHT(int, str):\n+    # comment\n+    # another comment\n+    # ChangeMe\n+EOF_TEST\n+\n+test_diff_funcname 'python: def' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+def RIGHT(pi: int = 3.14):\n+EOF_HUNK\n+def RIGHT(pi: int = 3.14):\n+    while True:\n+        break\n+    return ChangeMe()\n+EOF_TEST\n+\n+test_diff_funcname 'python: indented async def' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+async def RIGHT(self, x: int):\n+EOF_HUNK\n+class Foo:\n+    async def RIGHT(self, x: int):\n+        return [\n+            1,\n+            2,\n+            ChangeMe,\n+        ]\n+EOF_TEST\n+\n+test_diff_funcname 'python: indented class' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+class RIGHT:\n+EOF_HUNK\n+if TYPE_CHECKING:\n+    class RIGHT:\n+        # comment\n+        # another comment\n+        # ChangeMe\n+EOF_TEST\n+\n+test_diff_funcname 'python: indented def' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+def RIGHT(self, x: int):\n+EOF_HUNK\n+class Foo:\n+    def RIGHT(self, x: int):\n+        return [\n+            1,\n+            2,\n+            ChangeMe,\n+        ]\n+EOF_TEST\ndiff --git a/t/t4018/rust-fn b/t/t4018/rust-fn\ndeleted file mode 100644\nindex cbe02155f1..0000000000\n--- a/t/t4018/rust-fn\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-pub(self) fn RIGHT<T>(x: &[T]) where T: Debug {\n-    let _ = x;\n-    // a comment\n-    let a = ChangeMe;\n-}\ndiff --git a/t/t4018/rust-fn.ctx b/t/t4018/rust-fn.ctx\ndeleted file mode 100644\nindex baa37cf253..0000000000\n--- a/t/t4018/rust-fn.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-pub(self) fn RIGHT<T>(x: &[T]) where T: Debug {\ndiff --git a/t/t4018/rust-impl b/t/t4018/rust-impl\ndeleted file mode 100644\nindex 09df3cd93b..0000000000\n--- a/t/t4018/rust-impl\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-impl<'a, T: AsRef<[u8]>>  std::RIGHT for Git<'a> {\n-\n-    pub fn ChangeMe(&self) -> () {\n-    }\n-}\ndiff --git a/t/t4018/rust-impl.ctx b/t/t4018/rust-impl.ctx\ndeleted file mode 100644\nindex 5344c35f3f..0000000000\n--- a/t/t4018/rust-impl.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-impl<'a, T: AsRef<[u8]>>  std::RIGHT for Git<'a> {\ndiff --git a/t/t4018/rust-macro-rules b/t/t4018/rust-macro-rules\ndeleted file mode 100644\nindex ec610c5b62..0000000000\n--- a/t/t4018/rust-macro-rules\n+++ /dev/null\n@@ -1,6 +0,0 @@\n-macro_rules! RIGHT {\n-    () => {\n-        // a comment\n-        let x = ChangeMe;\n-    };\n-}\ndiff --git a/t/t4018/rust-macro-rules.ctx b/t/t4018/rust-macro-rules.ctx\ndeleted file mode 100644\nindex 7520463aa0..0000000000\n--- a/t/t4018/rust-macro-rules.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-macro_rules! RIGHT {\ndiff --git a/t/t4018/rust-struct b/t/t4018/rust-struct\ndeleted file mode 100644\nindex 76aff1c0d8..0000000000\n--- a/t/t4018/rust-struct\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-#[derive(Debug)]\n-pub(super) struct RIGHT<'a> {\n-    name: &'a str,\n-    age: ChangeMe,\n-}\ndiff --git a/t/t4018/rust-struct.ctx b/t/t4018/rust-struct.ctx\ndeleted file mode 100644\nindex c1e09dc808..0000000000\n--- a/t/t4018/rust-struct.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-pub(super) struct RIGHT<'a> {\ndiff --git a/t/t4018/rust-trait b/t/t4018/rust-trait\ndeleted file mode 100644\nindex ea397f09ed..0000000000\n--- a/t/t4018/rust-trait\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-unsafe trait RIGHT<T> {\n-    fn len(&self) -> u32;\n-    fn ChangeMe(&self, n: u32) -> T;\n-    fn iter<F>(&self, f: F) where F: Fn(T);\n-}\ndiff --git a/t/t4018/rust-trait.ctx b/t/t4018/rust-trait.ctx\ndeleted file mode 100644\nindex 6af803db29..0000000000\n--- a/t/t4018/rust-trait.ctx\n+++ /dev/null\n@@ -1 +0,0 @@\n-unsafe trait RIGHT<T> {\ndiff --git a/t/t4018/rust.sh b/t/t4018/rust.sh\nnew file mode 100755\nindex 0000000000..ba018c6b95\n--- /dev/null\n+++ b/t/t4018/rust.sh\n@@ -0,0 +1,60 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'rust: fn' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+pub(self) fn RIGHT<T>(x: &[T]) where T: Debug {\n+EOF_HUNK\n+pub(self) fn RIGHT<T>(x: &[T]) where T: Debug {\n+    let _ = x;\n+    // a comment\n+    let a = ChangeMe;\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'rust: impl' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+impl<'a, T: AsRef<[u8]>>  std::RIGHT for Git<'a> {\n+EOF_HUNK\n+impl<'a, T: AsRef<[u8]>>  std::RIGHT for Git<'a> {\n+\n+    pub fn ChangeMe(&self) -> () {\n+    }\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'rust: macro rules' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+macro_rules! RIGHT {\n+EOF_HUNK\n+macro_rules! RIGHT {\n+    () => {\n+        // a comment\n+        let x = ChangeMe;\n+    };\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'rust: struct' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+pub(super) struct RIGHT<'a> {\n+EOF_HUNK\n+#[derive(Debug)]\n+pub(super) struct RIGHT<'a> {\n+    name: &'a str,\n+    age: ChangeMe,\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'rust: trait' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+unsafe trait RIGHT<T> {\n+EOF_HUNK\n+unsafe trait RIGHT<T> {\n+    fn len(&self) -> u32;\n+    fn ChangeMe(&self, n: u32) -> T;\n+    fn iter<F>(&self, f: F) where F: Fn(T);\n+}\n+EOF_TEST\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416942","messageId":"20210215154427.32693-20-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215005236.11313-1-avarab@gmail.com","subject":"[PATCH v2 19/27] gitattributes doc: document multi-line userdiff patterns","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T15:44:19Z","receivedAt":"2021-02-15T16:20:05Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Document the multi-line userdiff patterns and how their matching and\nthe negation syntax works.\n\nThese patterns have been supported since f258475a6e (Per-path\nattribute based hunk header selection., 2007-07-06), and have had\ntheir current semantics ever since 3d8dccd74a (diff: fix \"multiple\nregexp\" semantics to find hunk header comment, 2008-09-20).\n\nBut we had no documentation for them, let's fix that, and also add\ntests showing how some of the things being discussed here work.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/gitattributes.txt | 33 +++++++++++++++++++---\n t/t4018/custom.sh               | 50 +++++++++++++++++++++++++++++++++\n t/t4018/perl.sh                 | 16 +++++++++++\n 3 files changed, 95 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\nindex 62c1147ba9..b51d2c86e0 100644\n--- a/Documentation/gitattributes.txt\n+++ b/Documentation/gitattributes.txt\n@@ -794,12 +794,37 @@ backslashes; the pattern above picks a line that begins with a\n backslash, and zero or more occurrences of `sub` followed by\n `section` followed by open brace, to the end of line.\n \n-There are built-in patterns shipped as part of git itself. A more\n-advanced version of the `tex` pattern discussed above is one of them.\n+Multiple patterns can be supplied by listing them one per line\n+separated by `\\n`. They will be matched one at a time from left to\n+right. Do not supply a trailing \"\\n\" for the last pattern. E.g.:\n+\n+------------------------\n+[diff \"perl\"]\n+\txfuncname = \"!^=head\\n^[^ ]+.*\"\n+------------------------\n+\n+Patterns in in a list of multiple that begin with \"!\" are negated. A\n+matching negated pattern will cause the matched line to be\n+skipped. Use it to skip a later pattern that would otherwise match. It\n+is an error if one or more negated patterns aren't followed by a\n+non-negated pattern.\n+\n+To match a literal \"!\" at the start of a line, use some other regex\n+construct that will match a literal \"!\" without \"!\" being the first\n+character on that line, such as \"[!]\".\n+\n+If the pattern contains a `$1` capture it will be used instead of the\n+entire matching line (`$0`) to display the hunk header. This can be\n+used e.g. to strip whitespace from the beginning of the line, or to\n+only display the function name as part of a longer function\n+definition.\n+\n+There are built-in patterns shipped as part of git itself, see the\n+full listing below.\n \n For built-in patterns, you do not need `diff.<lang>.xfuncname` in your\n-configuration file as discussed above, but if present, it will\n-override a built-in pattern.\n+configuration file. If present, it will override a built-in pattern,\n+as shown in the `diff.perl.xfuncname` example above.\n \n Nevertheless, you need to enable built-in patterns via .gitattributes`\n for the pattern to take effect.\ndiff --git a/t/t4018/custom.sh b/t/t4018/custom.sh\nindex 72d38dad68..30df13d8b2 100755\n--- a/t/t4018/custom.sh\n+++ b/t/t4018/custom.sh\n@@ -111,3 +111,53 @@ ChangeMe\n \n baz\n EOF_TEST\n+\n+test_expect_success 'custom: setup negation syntax, ! is magic' '\n+\tgit config diff.custom.xfuncname \"!negation\n+line\"\n+'\n+\n+test_diff_funcname 'custom: negation syntax, ! is magic' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+line\n+EOF_HUNK\n+line\n+!negation\n+\n+ChangeMe\n+\n+baz\n+EOF_TEST\n+\n+test_expect_success 'custom: setup negation syntax, use [!] to override ! magic' '\n+\tgit config diff.custom.xfuncname \"[!]negation\n+line\"\n+'\n+\n+test_diff_funcname 'custom: negation syntax, use [!] to override ! magic' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+!negation\n+EOF_HUNK\n+line\n+!negation\n+\n+ChangeMe\n+\n+baz\n+EOF_TEST\n+\n+test_expect_success 'custom: setup captures in multiple patterns' '\n+\tgit config diff.custom.xfuncname \"!^=head\n+^format ([^ ]+)\n+^sub ([^;]+)\"\n+'\n+\n+test_diff_funcname 'custom: captures in multiple patterns' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+foo\n+EOF_HUNK\n+sub foo;\n+=head1\n+ChangeMe\n+\n+EOF_TEST\ndiff --git a/t/t4018/perl.sh b/t/t4018/perl.sh\nindex ac8fff7417..2952483a2c 100755\n--- a/t/t4018/perl.sh\n+++ b/t/t4018/perl.sh\n@@ -76,3 +76,19 @@ sub RIGHT\n \tprint \"ChangeMe\\n\";\n }\n EOF_TEST\n+\n+\n+test_expect_success 'custom: setup config overrides built-in patterns' '\n+\tgit config diff.perl.xfuncname \"!^=head\n+^[^ ]+.*\"\n+'\n+\n+test_diff_funcname 'custom: config overrides built-in patterns' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+sub foo;\n+EOF_HUNK\n+sub foo;\n+=head1\n+ChangeMe\n+\n+EOF_TEST\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416943","messageId":"20210215154427.32693-16-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215005236.11313-1-avarab@gmail.com","subject":"[PATCH v2 15/27] userdiff tests: remove hack for \"RIGHT\" token","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T15:44:15Z","receivedAt":"2021-02-15T16:20:06Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Now that the \"RIGHT\" token isn't how we select the desired hunk header\nline in the test anymore we can revert a hack added in\nf1b75fbaf1 (t4018: convert custom pattern test to the new\ninfrastructure, 2014-03-21) and go back to the regular expression we\nwere testing before that change.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018/custom.sh | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/t/t4018/custom.sh b/t/t4018/custom.sh\nindex 59d855c01c..b208a771d2 100755\n--- a/t/t4018/custom.sh\n+++ b/t/t4018/custom.sh\n@@ -33,14 +33,14 @@ public class Beer\n EOF_TEST\n \n test_expect_success 'custom: setup match to end of line' '\n-\tgit config diff.custom.funcname \"......Beer\\$\"\n+\tgit config diff.custom.funcname \"Beer\\$\"\n '\n \n test_diff_funcname 'custom: match to end of line' \\\n \t8<<\\EOF_HUNK 9<<\\EOF_TEST\n-RIGHT_Beer\n+Beer\n EOF_HUNK\n-public class RIGHT_Beer\n+public class Beer\n {\n \tint special;\n \tpublic static void main(String args[])\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416944","messageId":"20210215154427.32693-18-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215005236.11313-1-avarab@gmail.com","subject":"[PATCH v2 17/27] userdiff tests + docs: document & test \"diff.<driver>.x?funcname\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T15:44:17Z","receivedAt":"2021-02-15T16:20:47Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add the missing documentation for \"diff.<driver>.funcname\" and test\nfor how it and \"diff.<driver>.xfuncname\" interact.\n\nBetween the introduction of the \"diff.<driver>.xfuncname\" form in\n45d9414fa5 (diff.*.xfuncname which uses \"extended\" regex's for hunk\nheader selection, 2008-09-18) and when this documentation was written\nin 90b94c26f7 (Documentation: Add diff.<driver>.* to config,\n2011-04-07) we forgot to document the existence of\n\"diff.<driver>.funcname\".\n\nLet's make a mention of it here, we could also partially revert the\nformer commit and discuss the more verbose form in gitattributes(5),\nbut let's stop short of that. It makes sense to guide users towards\nERE over BRE whenever possible.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/config/diff.txt | 11 +++++++++++\n t/t4018/custom.sh             | 34 ++++++++++++++++++++++++++++++++++\n 2 files changed, 45 insertions(+)\n\ndiff --git a/Documentation/config/diff.txt b/Documentation/config/diff.txt\nindex 2d3331f55c..6f39ef1da9 100644\n--- a/Documentation/config/diff.txt\n+++ b/Documentation/config/diff.txt\n@@ -153,10 +153,21 @@ diff.<driver>.command::\n \tThe custom diff driver command.  See linkgit:gitattributes[5]\n \tfor details.\n \n+diff.<driver>.funcname::\n diff.<driver>.xfuncname::\n \tThe regular expression that the diff driver should use to\n \trecognize the hunk header.  A built-in pattern may also be used.\n \tSee linkgit:gitattributes[5] for details.\n++\n+When provided as `diff.<driver>.funcname` the regular expression is\n+interpreted as a basic regular expression. With\n+`diff.<driver>.xfuncname` it's interpreted as an extended regular\n+expression.\n++\n+The `*.funcname` and `*.xfuncname` variables behave as if though they\n+were one configuration variable for the purposes of what value\n+eventually gets used. Setting `*.funcname` will override an earlier\n+`*.xfuncname` and vice-versa.\n \n diff.<driver>.binary::\n \tSet this option to true to make the diff driver treat files as\ndiff --git a/t/t4018/custom.sh b/t/t4018/custom.sh\nindex b208a771d2..72d38dad68 100755\n--- a/t/t4018/custom.sh\n+++ b/t/t4018/custom.sh\n@@ -77,3 +77,37 @@ public class Beer\n \t}\n }\n EOF_TEST\n+\n+test_expect_success 'custom: setup config precedence' '\n+\tgit config diff.custom.funcname \"foo\" &&\n+\tgit config diff.custom.xfuncname \"bar\"\n+'\n+\n+test_diff_funcname 'custom: config precedence' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+bar\n+EOF_HUNK\n+foo\n+bar\n+\n+ChangeMe\n+\n+baz\n+EOF_TEST\n+\n+test_expect_success 'custom: setup config precedence' '\n+\tgit config diff.custom.xfuncname \"bar\" &&\n+\tgit config diff.custom.funcname \"foo\"\n+'\n+\n+test_diff_funcname 'custom: config precedence' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+foo\n+EOF_HUNK\n+foo\n+bar\n+\n+ChangeMe\n+\n+baz\n+EOF_TEST\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416945","messageId":"20210215154427.32693-22-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215005236.11313-1-avarab@gmail.com","subject":"[PATCH v2 21/27] userdiff tests: factor out test_diff_funcname() logic","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T15:44:21Z","receivedAt":"2021-02-15T16:20:56Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Factor out logic in test_diff_funcname() into two helper functions,\nthese will be useful in a follow-up commit where we'll do this munging\nin more than one place.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018-diff-funcname.sh | 16 +++++++++++++---\n 1 file changed, 13 insertions(+), 3 deletions(-)\n\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex 3ba9d657b1..2efe4e5bdd 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -41,6 +41,17 @@ test_expect_success 'last regexp must not be negated' '\n \ttest_i18ngrep \": Last expression must not be negated:\" msg\n '\n \n+do_change_me () {\n+\tfile=$1\n+\tsed -e \"s/ChangeMe/IWasChanged/\" <\"$file\" >tmp &&\n+\tmv tmp \"$file\"\n+}\n+\n+last_diff_context_line () {\n+\tfile=$1\n+\tsed -n -e \"s/^.*@@$//p\" -e \"s/^.*@@ //p\" <$file\n+}\n+\n test_diff_funcname () {\n \tdesc=$1\n \tcat <&8 >arg.header &&\n@@ -51,13 +62,12 @@ test_diff_funcname () {\n \t\tcp arg.test \"$what\" &&\n \t\tcp arg.header expected &&\n \t\tgit add \"$what\" &&\n-\t\tsed -e \"s/ChangeMe/IWasChanged/\" <\"$what\" >tmp &&\n-\t\tmv tmp \"$what\"\n+\t\tdo_change_me \"$what\"\n \t' &&\n \n \ttest_expect_success \"$desc\" '\n \t\tgit diff -U1 \"$what\" >diff &&\n-\t\tsed -n -e \"s/^.*@@$//p\" -e \"s/^.*@@ //p\" <diff >actual &&\n+\t\tlast_diff_context_line diff >actual &&\n \t\ttest_cmp expected actual\n \t' &&\n \n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416946","messageId":"20210215154427.32693-23-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215005236.11313-1-avarab@gmail.com","subject":"[PATCH v2 22/27] userdiff tests: test hunk headers on accumulated files","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T15:44:22Z","receivedAt":"2021-02-15T16:21:23Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The existing tests in \"t/t4018/\" are unrealistic in that they're all\nsetting up small few-line isolated test cases with one thing we could\nmatch as a hunk header, right above the one change in the file.\n\nExpand those tests by accumulating changes within the same file type\nin the \"test_diff_funcname\" function. So e.g. for \"bash\" we'll end up\na \"bash.acc\" file with 15 s/ChangeMe/IWasChanged/ changes.\n\nThis stress tests whether the hunk header selection will \"jump across\"\nto an earlier change because the match for that is greedier.\n\nAs it turns out we had one false positive in \"t/t4018/cpp.sh\" and\n\"t4018/matlab.sh\" because of how the tests were structured, we must\nalways give the \"ChangeMe\" line at least one line of separation from\nthe header, since it was at the end of those tests we'd select the\n\"wrong\" header. Let's adjust the spacing to compensate.\n\nSo in the end we found nothing of interest here, regardless, I think\nit is useful to continue to test in this mode. It's likely to aid in\nfinding bugs in combinations of our positive and negative matching as\nwe add more built-in patterns.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018-diff-funcname.sh | 19 +++++++++++++++++++\n t/t4018/cpp.sh           |  1 +\n t/t4018/matlab.sh        |  3 +++\n 3 files changed, 23 insertions(+)\n\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex 2efe4e5bdd..8b4500037f 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -65,12 +65,26 @@ test_diff_funcname () {\n \t\tdo_change_me \"$what\"\n \t' &&\n \n+\ttest_expect_success \"setup: $desc (accumulated)\" '\n+\t\tcat arg.test >>arg.tests &&\n+\t\tcp arg.tests \"$what\".acc &&\n+\t\tgit add \"$what\".acc &&\n+\t\tdo_change_me \"$what\".acc\n+\t' &&\n+\n \ttest_expect_success \"$desc\" '\n \t\tgit diff -U1 \"$what\" >diff &&\n \t\tlast_diff_context_line diff >actual &&\n \t\ttest_cmp expected actual\n \t' &&\n \n+\ttest_expect_success \"$desc (accumulated)\" '\n+\t\tgit diff -U1 \"$what\".acc >diff &&\n+\t\tlast_diff_context_line diff >actual.lines &&\n+\t\ttail -n 1 actual.lines >actual &&\n+\t\ttest_cmp expected actual\n+\t' &&\n+\n \ttest_expect_success \"teardown: $desc\" '\n \t\t# In case any custom config was set immediately before\n \t\t# the test itself in the test file\n@@ -93,6 +107,11 @@ do\n \t\techo \"$what\" >arg.what\n \t' &&\n \n+\ttest_expect_success \"setup: hunk header for $what (accumulated)\" '\n+\t\t>arg.tests &&\n+\t\techo \"$what.acc diff=$what\" >>.gitattributes\n+\t' &&\n+\n \t. \"$test\"\n done\n \ndiff --git a/t/t4018/cpp.sh b/t/t4018/cpp.sh\nindex 185d40d5ef..e0ab749316 100755\n--- a/t/t4018/cpp.sh\n+++ b/t/t4018/cpp.sh\n@@ -206,6 +206,7 @@ void wrong()\n struct RIGHT_iterator_tag {};\n \n int ChangeMe;\n+\n EOF_TEST\n \n test_diff_funcname 'cpp: template function definition' \\\ndiff --git a/t/t4018/matlab.sh b/t/t4018/matlab.sh\nindex f62289148e..fba410e6f5 100755\n--- a/t/t4018/matlab.sh\n+++ b/t/t4018/matlab.sh\n@@ -31,6 +31,7 @@ EOF_HUNK\n %%% RIGHT section\n # this is octave script\n ChangeMe = 1;\n+\n EOF_TEST\n \n test_diff_funcname 'matlab: octave section 2' \\\n@@ -40,6 +41,7 @@ EOF_HUNK\n ## RIGHT section\n # this is octave script\n ChangeMe = 1;\n+\n EOF_TEST\n \n test_diff_funcname 'matlab: section' \\\n@@ -49,4 +51,5 @@ EOF_HUNK\n %% RIGHT section\n % this is understood by both matlab and octave\n ChangeMe = 1;\n+\n EOF_TEST\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416948","messageId":"4bd7bb84-3b75-258e-b488-f66dff6ba6b5@kdbg.org","threadId":"55144","inReplyTo":"20210215154427.32693-10-avarab@gmail.com","subject":"Re: [PATCH v2 09/27] userdiff tests: match full hunk headers","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2021-02-15T17:13:18Z","receivedAt":"2021-02-15T17:14:39Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 15.02.21 um 16:44 schrieb Ævar Arnfjörð Bjarmason:\n> Fix a regression in the test framework for userdiff added in\n> bfa7d01413 (t4018: an infrastructure to test hunk headers,\n> 2014-03-21).\n> \n> The testing infrastructure added in that change went overboard with\n> simplifying the tests, to the point where we lost test coverage.\n> \n> Before that we'd been able to test the full context line, or ever\n> since the feature was originally added in f258475a6e (Per-path\n> attribute based hunk header selection., 2007-07-06).\n> \n> After bfa7d01413 all we cared about was whether \"RIGHT\" appeared on\n> the line. We thus lost the information about whether or not \"RIGHT\"\n> was extracted from the line for the hunk header, or the line appeared\n> in full (or other subset of the line).\n> \n> Let's bring back coverage for that by adding corresponding *.ctx\n> files, this has the added advantage that we're doing a \"test_cmp\", so\n> when we have failures it's just a non-zero exit code from \"grep\",\n> we'll actually have something meaningful in the \"-v\" output.\n> \n> As we'll see in a follow-up commit this is an intermediate step\n> towards even better test coverage. I'm structuring these tests in such\n> a way as to benefit from the diff.colorMove detection.\n> \n> The \"sed -n -e\" here was originally a single 's/^.*@@\\( \\|$\\)//p'\n> pattern, but the '\\( \\|$\\)' part had portability issues on OSX and\n> AIX.\n> \n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>   t/t4018-diff-funcname.sh                      |  7 +++---\n>   t/t4018/README                                | 22 +++++++++----------\n>   t/t4018/README.ctx                            |  1 +\n>   t/t4018/bash-arithmetic-function.ctx          |  1 +\n>   t/t4018/bash-bashism-style-compact.ctx        |  1 +\n>   [...and so on...]\n\nThis is what I meant by \"without burdening test writers with lots of\nsubtleties\".\n\nI'm not a friend of this change :-(\n\nI think you are going overboard with required test precision. To have \nuseful tests for userdiff patterns that demonstrate its features, \nauthors should write *many* tests. The right balance should be on the \ncoverage of userdiff pattern features, not on the subtle details of each \nand everyone of it. Requiring that many additional context files makes \nit *really hard* to comply.\n\n-- Hannes\n"},{"id":"416949","messageId":"c1e637d5-ad69-1726-2b38-476d0192ca02@kdbg.org","threadId":"55144","inReplyTo":"20210215154427.32693-12-avarab@gmail.com","subject":"Re: [PATCH v2 11/27] blame tests: simplify userdiff driver test","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2021-02-15T17:23:49Z","receivedAt":"2021-02-15T17:26:33Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 15.02.21 um 16:44 schrieb Ævar Arnfjörð Bjarmason:\n> Simplify the test added in 9466e3809d (blame: enable funcname blaming\n> with userdiff driver, 2020-11-01) to use the --author support recently\n> added in 999cfc4f45 (test-lib functions: add --author support to\n> test_commit, 2021-01-12).\n> \n> We also did not need the full fortran-external-function content, let's\n> cut it down to just the important parts, and further modify it to\n> demonstrate that the fortran-specific userdiff function is in effect\n> by adding \"WRONG\" lines surrounding the \"RIGHT\" one.\n> \n> The test also left behind a .gitattributes files, let's clean it up\n> with \"test_when_finished\".\n> \n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>   t/annotate-tests.sh | 36 +++++++++++++++---------------------\n>   1 file changed, 15 insertions(+), 21 deletions(-)\n> \n> diff --git a/t/annotate-tests.sh b/t/annotate-tests.sh\n> index 04a2c58594..4a86e0f349 100644\n> --- a/t/annotate-tests.sh\n> +++ b/t/annotate-tests.sh\n> @@ -479,32 +479,26 @@ test_expect_success 'blame -L ^:RE (absolute: end-of-file)' '\n>   \tcheck_count -f hello.c -L$n -L^:ma.. F 4 G 1 H 1\n>   '\n>   \n> -test_expect_success 'setup -L :funcname with userdiff driver' '\n> -\techo \"fortran-* diff=fortran\" >.gitattributes &&\n> -\tfortran_file=fortran-external-function &&\n> -\tcat >$fortran_file <<-\\EOF &&\n> +test_expect_success 'blame -L :funcname with userdiff driver' '\n> +\tcat >file.template <<-\\EOF &&\n> +\tdef WRONG begin end\n>   \tfunction RIGHT(a, b) result(c)\n> +\tint WRONG(void) {}\n>   \n>   \tinteger, intent(in) :: ChangeMe\n> -\tinteger, intent(in) :: b\n> -\tinteger, intent(out) :: c\n> -\n> -\tc = a+b\n> -\n> -\tend function RIGHT\n>   \tEOF\n> -\tgit add \"$fortran_file\" &&\n> -\tGIT_AUTHOR_NAME=\"A\" GIT_AUTHOR_EMAIL=\"A@test.git\" \\\n> -\tgit commit -m \"add fortran file\" &&\n> -\tsed -e \"s/ChangeMe/IWasChanged/\" <\"$fortran_file\" >\"$fortran_file\".tmp &&\n> -\tmv \"$fortran_file\".tmp \"$fortran_file\" &&\n> -\tgit add \"$fortran_file\" &&\n> -\tGIT_AUTHOR_NAME=\"B\" GIT_AUTHOR_EMAIL=\"B@test.git\" \\\n> -\tgit commit -m \"change fortran file\"\n> -'\n>   \n> -test_expect_success 'blame -L :funcname with userdiff driver' '\n> -\tcheck_count -f fortran-external-function -L:RIGHT A 7 B 1\n> +\tfortran_file=file.f03 &&\n> +\ttest_when_finished \"rm .gitattributes\" &&\n> +\techo \"$fortran_file diff=fortran\" >.gitattributes &&\n> +\n> +\ttest_commit --author \"A <A@test.git>\" \\\n> +\t\t\"add\" $fortran_file \\\n> +\t\t\"$(cat file.template)\" &&\n> +\ttest_commit --author \"B <B@test.git>\" \\\n> +\t\t\"change\" $fortran_file \\\n> +\t\t\"$(cat file.template | sed -e s/ChangeMe/IWasChanged/)\" &&\n> +\tcheck_count -f $fortran_file -L:RIGHT A 3 B 1\n>   '\n>   \n>   test_expect_success 'setup incremental' '\n> \n\nI don't get the point. What do you need the tokens \"WRONG\" for when they \nare not checked anywhere? Instead of adding unrelated lines (that do not \neven look like Fortran), couldn't you just not remove some of the \nothers? In particular, the last one that contains \"RIGHT\" as well may be \nuseful to keep in order to show that the code is not confused by it.\n\nPlease place \"$fortran_file\" in dquotes on the check_count line.\n\n-- Hannes\n"},{"id":"416950","messageId":"CAPig+cROa_77Wb4z3+-7WVQ0f=PQMvN-QmdOhqkMm28Gz7qsAw@mail.gmail.com","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"Re: [PATCH v2 00/27] userdiff: refactor + test + doc + misc improvements","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-02-15T17:45:06Z","receivedAt":"2021-02-15T17:47:12Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Feb 15, 2021 at 10:44 AM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n> Incorporates all the feedback on v2 and more, see the range-diff\n> below.\n\nI hadn't finished reading the previous version of this series...\nNevertheless, see a few issues below which I noticed while scanning\nthe range-diff...\n\n>     ++expect to appear in the hunk header. We munged away the starting \"@@\n>     ++[...] @@\" part of the line for ease of not having to hardcode the line\n>     ++numbers and offsets.\n\nNit: \"munge\" is an oddball word to use here. \"We strip away the\nstarting...\" would be simpler, but perhaps it doesn't matter too much\nas this documentation is aimed at developers, not end users.\n\n>     ++For built-in patterns, you do not need `diff.<lang>.xfuncname` in your\n>     ++configuration file as discussed above, but if present, it will\n>     ++override a built-in pattern.\n>      +\n>     -+You still need to enable built-in patterns with the the attribute\n>     -+mechanism, via `.gitattributes`).\n>     ++Nevertheless, you need to enable built-in patterns via .gitattributes`\n>     ++for the pattern to take effect.\n\nMissing opening backtick on `.gitattributes`.\n\n>     ++Patterns in in a list of multiple that begin with \"!\" are negated. A\n>     ++matching negated pattern will cause the matched line to be\n>     ++skipped. Use it to skip a later pattern that would otherwise match. It\n>     ++is an error if one or more negated patterns aren't followed by a\n>     ++non-negated pattern.\n\ns/in in/in/\n\nAlso, \"of multiple\" what?\n\n>     ++To match a literal \"!\" at the start of a line, use some other regex\n>     ++construct that will match a literal \"!\" without \"!\" being the first\n>     ++character on that line, such as \"[!]\".\n\nOverall, I find this description easier to read and understand than in\nthe previous version.\n"},{"id":"416951","messageId":"0f70d5e4-cf35-1e67-be02-2baf3036d471@kdbg.org","threadId":"55144","inReplyTo":"20210215154427.32693-13-avarab@gmail.com","subject":"Re: [PATCH v2 12/27] userdiff tests: rewrite hunk header test infrastructure","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2021-02-15T17:53:53Z","receivedAt":"2021-02-15T17:54:17Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 15.02.21 um 16:44 schrieb Ævar Arnfjörð Bjarmason:\n> Rewrite the hunk header test infrastructure introduced in\n> bfa7d01413 (t4018: an infrastructure to test hunk headers,\n> 2014-03-21). See c228a5c077 (Merge branch 'js/userdiff-cc',\n> 2014-03-31) for the whole series that commit was part of.\n> \n> As noted in an earlier commit that change introduced the regression of\n> not testing for the full hunk line, but just whether \"RIGHT\" appeared\n> on it[1]. A preceding commit fixed that specific issue, but we were\n> still left with the inflexibility of the approach described in the\n> now-deleted t/t4018/README.\n> \n> I.e. to add any sort of new tests that used the existing test data\n> we'd either need to add more files like the recently added (but now\n> deleted) *.ctx) files, using the filesystem as our test datastructure,\n> or introduce more parsing for the custom file format we were growing\n> here.\n> \n> Let's instead just move this over to using a custom test\n> function. This makes it trivial to add new tests by adding new\n> optional parameters to the function. Let's still keep the relevant\n> files in the \"t/t4018/\" subdirectory instead of adding ~1.5k\n> lines (and growing) to \"t/t4018-diff-funcname.sh\"\n> \n> If this diff is viewed with \"--color-moved=plain\" we can see that\n> there's no changes to the lines being moved into the new *.sh files,\n> i.e. all the deletions are moves. I'm just adding boilerplate around\n> those existing lines.\n> \n> The one-off refactoring was performed by an ad-hoc shellscript [2].\n> \n> 1. https://lore.kernel.org/git/87wnvbbf2y.fsf@evledraar.gmail.com/\n> 2.\n> \t#!/bin/sh\n> \tset -ex\n> \n> \tgit rm README*\n> \tfor t in $(git ls-files ':!*.ctx')\n> \tdo\n> \t\tlang=$(echo $t | sed 's/-.*//')\n> \t\tdesc=$(echo $t | sed -E 's/^[^-]*-//' | tr - \" \")\n> \n> \t\tif ! test -e $lang.sh\n> \t\tthen\n> \t\t\tcat >$lang.sh <<-EOF\n> \t\t\t#!/bin/sh\n> \t\t\t#\n> \t\t\t# See ../t4018-diff-funcname.sh's test_diff_funcname()\n> \t\t\t#\n> \n> \t\t\tEOF\n> \t\telse\n> \t\t\techo >>$lang.sh\n> \t        fi\n> \n> \t\t(\n> \t            printf \"test_diff_funcname '%s: %s' \\\\\" \"$lang\" \"$desc\"\n> \t            echo\n> \t            printf \"\\t8<<%sEOF_HUNK 9<<%sEOF_TEST\\n\" '\\' '\\'\n> \t            cat $t.ctx\n> \t            printf \"EOF_HUNK\\n\"\n> \t            cat $t\n> \t            printf \"EOF_TEST\\n\"\n> \t\t) >>$lang.sh\n> \n> \t\tchmod +x $lang.sh\n> \t\tgit add $lang.sh\n> \t        git rm $t $t.ctx\n> \tdone\n> \n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n\n> diff --git a/t/t4018/bash.sh b/t/t4018/bash.sh\n> new file mode 100755\n> index 0000000000..69144d9144\n> --- /dev/null\n> +++ b/t/t4018/bash.sh\n> @@ -0,0 +1,160 @@\n> +#!/bin/sh\n> +#\n> +# See ../t4018-diff-funcname.sh's test_diff_funcname()\n> +#\n> +\n> +test_diff_funcname 'bash: arithmetic function' \\\n> +\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n> +RIGHT()\n> +EOF_HUNK\n> +RIGHT() ((\n> +\n> +    ChangeMe = \"$x\" + \"$y\"\n> +))\n> +EOF_TEST\n> +\n> +test_diff_funcname 'bash: bashism style compact' \\\n> +\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n> +function RIGHT {\n> +EOF_HUNK\n> +function RIGHT {\n> +    function InvalidSyntax{\n> +        :\n> +        echo 'ChangeMe'\n> +    }\n> +}\n> +EOF_TEST\n> +\n> +test_diff_funcname 'bash: bashism style function' \\\n> +\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n> +function RIGHT {\n> +EOF_HUNK\n> +function RIGHT {\n> +    :\n> +    echo 'ChangeMe'\n> +}\n> +EOF_TEST\n> [...]\n\nThat is not my dream of \"simple\". But I'm not a userdiff author anymore, \nso...\n\nI don't know, yet, where this is heading to what the advantage is. At \nany rate, \"trivial to add new tests\" was also the case when each test \ncase was in its own file. Without the boilerplate!\n\n-- Hannes\n"},{"id":"416952","messageId":"7984b3d6-7c01-565e-f5fd-3104af4b3ce6@kdbg.org","threadId":"55144","inReplyTo":"20210215154427.32693-20-avarab@gmail.com","subject":"Re: [PATCH v2 19/27] gitattributes doc: document multi-line userdiff patterns","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2021-02-15T18:18:54Z","receivedAt":"2021-02-15T18:20:05Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 15.02.21 um 16:44 schrieb Ævar Arnfjörð Bjarmason:\n> diff --git a/t/t4018/custom.sh b/t/t4018/custom.sh\n> index 72d38dad68..30df13d8b2 100755\n> --- a/t/t4018/custom.sh\n> +++ b/t/t4018/custom.sh\n> @@ -111,3 +111,53 @@ ChangeMe\n>   \n>   baz\n>   EOF_TEST\n> +\n> +test_expect_success 'custom: setup negation syntax, ! is magic' '\n> +\tgit config diff.custom.xfuncname \"!negation\n> +line\"\n> +'\n> +\n> +test_diff_funcname 'custom: negation syntax, ! is magic' \\\n> +\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n> +line\n> +EOF_HUNK\n> +line\n> +!negation\n> +\n> +ChangeMe\n> +\n> +baz\n> +EOF_TEST\n> +\n> +test_expect_success 'custom: setup negation syntax, use [!] to override ! magic' '\n> +\tgit config diff.custom.xfuncname \"[!]negation\n> +line\"\n> +'\n> +\n> +test_diff_funcname 'custom: negation syntax, use [!] to override ! magic' \\\n> +\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n> +!negation\n> +EOF_HUNK\n> +line\n> +!negation\n> +\n> +ChangeMe\n> +\n> +baz\n> +EOF_TEST\n> +\n> +test_expect_success 'custom: setup captures in multiple patterns' '\n> +\tgit config diff.custom.xfuncname \"!^=head\n> +^format ([^ ]+)\n> +^sub ([^;]+)\"\n> +'\n> +\n> +test_diff_funcname 'custom: captures in multiple patterns' \\\n> +\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n> +foo\n> +EOF_HUNK\n> +sub foo;\n> +=head1\n> +ChangeMe\n> +\n> +EOF_TEST\n\nThis test would not catch a regression. You must leave a line between \nthe candidate-that-must-not-be, =head1, and ChangeMe, otherwise, =head1 \nis never tested against the negation pattern.\n\nOr did you change the diff invocation in an earlier patch such that it \ndoes not emit context lines?\n\n> diff --git a/t/t4018/perl.sh b/t/t4018/perl.sh\n> index ac8fff7417..2952483a2c 100755\n> --- a/t/t4018/perl.sh\n> +++ b/t/t4018/perl.sh\n> @@ -76,3 +76,19 @@ sub RIGHT\n>   \tprint \"ChangeMe\\n\";\n>   }\n>   EOF_TEST\n> +\n> +\n> +test_expect_success 'custom: setup config overrides built-in patterns' '\n> +\tgit config diff.perl.xfuncname \"!^=head\n> +^[^ ]+.*\"\n> +'\n> +\n> +test_diff_funcname 'custom: config overrides built-in patterns' \\\n> +\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n> +sub foo;\n> +EOF_HUNK\n> +sub foo;\n> +=head1\n> +ChangeMe\n> +\n> +EOF_TEST\n\nSame here.\n\n-- Hannes\n"},{"id":"416956","messageId":"eebdd570-ba21-037c-c608-ec0989956bfa@kdbg.org","threadId":"55144","inReplyTo":"20210215154427.32693-23-avarab@gmail.com","subject":"Re: [PATCH v2 22/27] userdiff tests: test hunk headers on accumulated files","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2021-02-15T18:29:44Z","receivedAt":"2021-02-15T18:30:22Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 15.02.21 um 16:44 schrieb Ævar Arnfjörð Bjarmason:\n> The existing tests in \"t/t4018/\" are unrealistic in that they're all\n> setting up small few-line isolated test cases with one thing we could\n> match as a hunk header, right above the one change in the file.\n> \n> Expand those tests by accumulating changes within the same file type\n> in the \"test_diff_funcname\" function. So e.g. for \"bash\" we'll end up\n> a \"bash.acc\" file with 15 s/ChangeMe/IWasChanged/ changes.\n> \n> This stress tests whether the hunk header selection will \"jump across\"\n> to an earlier change because the match for that is greedier.\n> \n> As it turns out we had one false positive in \"t/t4018/cpp.sh\" and\n> \"t4018/matlab.sh\" because of how the tests were structured, we must\n> always give the \"ChangeMe\" line at least one line of separation from\n> the header, since it was at the end of those tests we'd select the\n> \"wrong\" header. Let's adjust the spacing to compensate.\n\nI can't wrap my head around this. The ChangeMe lines are one line away \nfrom the RIGHT lines. Why would we need a line below ChangeMe?\n\nOr is this just a fall-out from this change itself? Then I'd appreciate \nto describe it as \"This change now triggers false positives in... \nbecause... Let's adjust the spacing compensate.\".\n\n> \n> So in the end we found nothing of interest here, regardless, I think\n> it is useful to continue to test in this mode. It's likely to aid in\n> finding bugs in combinations of our positive and negative matching as\n> we add more built-in patterns.\n> \n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>   t/t4018-diff-funcname.sh | 19 +++++++++++++++++++\n>   t/t4018/cpp.sh           |  1 +\n>   t/t4018/matlab.sh        |  3 +++\n>   3 files changed, 23 insertions(+)\n> \n> diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\n> index 2efe4e5bdd..8b4500037f 100755\n> --- a/t/t4018-diff-funcname.sh\n> +++ b/t/t4018-diff-funcname.sh\n> @@ -65,12 +65,26 @@ test_diff_funcname () {\n>   \t\tdo_change_me \"$what\"\n>   \t' &&\n>   \n> +\ttest_expect_success \"setup: $desc (accumulated)\" '\n> +\t\tcat arg.test >>arg.tests &&\n> +\t\tcp arg.tests \"$what\".acc &&\n> +\t\tgit add \"$what\".acc &&\n> +\t\tdo_change_me \"$what\".acc\n> +\t' &&\n> +\n>   \ttest_expect_success \"$desc\" '\n>   \t\tgit diff -U1 \"$what\" >diff &&\n>   \t\tlast_diff_context_line diff >actual &&\n>   \t\ttest_cmp expected actual\n>   \t' &&\n>   \n> +\ttest_expect_success \"$desc (accumulated)\" '\n> +\t\tgit diff -U1 \"$what\".acc >diff &&\n> +\t\tlast_diff_context_line diff >actual.lines &&\n> +\t\ttail -n 1 actual.lines >actual &&\n> +\t\ttest_cmp expected actual\n> +\t' &&\n> +\n>   \ttest_expect_success \"teardown: $desc\" '\n>   \t\t# In case any custom config was set immediately before\n>   \t\t# the test itself in the test file\n> @@ -93,6 +107,11 @@ do\n>   \t\techo \"$what\" >arg.what\n>   \t' &&\n>   \n> +\ttest_expect_success \"setup: hunk header for $what (accumulated)\" '\n> +\t\t>arg.tests &&\n> +\t\techo \"$what.acc diff=$what\" >>.gitattributes\n> +\t' &&\n> +\n>   \t. \"$test\"\n>   done\n>   \n> diff --git a/t/t4018/cpp.sh b/t/t4018/cpp.sh\n> index 185d40d5ef..e0ab749316 100755\n> --- a/t/t4018/cpp.sh\n> +++ b/t/t4018/cpp.sh\n> @@ -206,6 +206,7 @@ void wrong()\n>   struct RIGHT_iterator_tag {};\n>   \n>   int ChangeMe;\n> +\n>   EOF_TEST\n>   \n>   test_diff_funcname 'cpp: template function definition' \\\n> diff --git a/t/t4018/matlab.sh b/t/t4018/matlab.sh\n> index f62289148e..fba410e6f5 100755\n> --- a/t/t4018/matlab.sh\n> +++ b/t/t4018/matlab.sh\n> @@ -31,6 +31,7 @@ EOF_HUNK\n>   %%% RIGHT section\n>   # this is octave script\n>   ChangeMe = 1;\n> +\n>   EOF_TEST\n>   \n>   test_diff_funcname 'matlab: octave section 2' \\\n> @@ -40,6 +41,7 @@ EOF_HUNK\n>   ## RIGHT section\n>   # this is octave script\n>   ChangeMe = 1;\n> +\n>   EOF_TEST\n>   \n>   test_diff_funcname 'matlab: section' \\\n> @@ -49,4 +51,5 @@ EOF_HUNK\n>   %% RIGHT section\n>   % this is understood by both matlab and octave\n>   ChangeMe = 1;\n> +\n>   EOF_TEST\n> \n\n-- Hannes\n"},{"id":"416963","messageId":"87o8glb20j.fsf@evledraar.gmail.com","threadId":"55144","inReplyTo":"4bd7bb84-3b75-258e-b488-f66dff6ba6b5@kdbg.org","subject":"Re: [PATCH v2 09/27] userdiff tests: match full hunk headers","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T18:48:44Z","receivedAt":"2021-02-15T18:49:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, Feb 15 2021, Johannes Sixt wrote:\n\n> Am 15.02.21 um 16:44 schrieb Ævar Arnfjörð Bjarmason:\n>> Fix a regression in the test framework for userdiff added in\n>> bfa7d01413 (t4018: an infrastructure to test hunk headers,\n>> 2014-03-21).\n>> The testing infrastructure added in that change went overboard with\n>> simplifying the tests, to the point where we lost test coverage.\n>> Before that we'd been able to test the full context line, or ever\n>> since the feature was originally added in f258475a6e (Per-path\n>> attribute based hunk header selection., 2007-07-06).\n>> After bfa7d01413 all we cared about was whether \"RIGHT\" appeared on\n>> the line. We thus lost the information about whether or not \"RIGHT\"\n>> was extracted from the line for the hunk header, or the line appeared\n>> in full (or other subset of the line).\n>> Let's bring back coverage for that by adding corresponding *.ctx\n>> files, this has the added advantage that we're doing a \"test_cmp\", so\n>> when we have failures it's just a non-zero exit code from \"grep\",\n>> we'll actually have something meaningful in the \"-v\" output.\n>> As we'll see in a follow-up commit this is an intermediate step\n>> towards even better test coverage. I'm structuring these tests in such\n>> a way as to benefit from the diff.colorMove detection.\n>> The \"sed -n -e\" here was originally a single 's/^.*@@\\( \\|$\\)//p'\n>> pattern, but the '\\( \\|$\\)' part had portability issues on OSX and\n>> AIX.\n>> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>> ---\n>>   t/t4018-diff-funcname.sh                      |  7 +++---\n>>   t/t4018/README                                | 22 +++++++++----------\n>>   t/t4018/README.ctx                            |  1 +\n>>   t/t4018/bash-arithmetic-function.ctx          |  1 +\n>>   t/t4018/bash-bashism-style-compact.ctx        |  1 +\n>>   [...and so on...]\n>\n> This is what I meant by \"without burdening test writers with lots of\n> subtleties\".\n>\n> I'm not a friend of this change :-(\n>\n> I think you are going overboard with required test precision. To have\n> useful tests for userdiff patterns that demonstrate its features, \n> authors should write *many* tests. The right balance should be on the\n> coverage of userdiff pattern features, not on the subtle details of\n> each and everyone of it. Requiring that many additional context files\n> makes it *really hard* to comply.\n\nI agree that this change sucks when viewed in isolation. I think it has\nvalue as part of the larger series, since (as noted in the commit\nmessage) the point here is to allow the reviewer to see the the test\ncontent & semantics aren't different.\n\nI suppose I could do some version of squashing this and 12/27, but then\nI'd be introducing the full testing of the context line at the same\ntime.\n\nBy doing it this way I split the change in test semantics from the test\nstructure change itself in 12/27.\n"},{"id":"416964","messageId":"d02c99b8-ae26-9804-480c-eae880f4a6cb@web.de","threadId":"55144","inReplyTo":"20210215155020.2804-2-avarab@gmail.com","subject":"Re: [PATCH 1/2] diff: do not display hunk context under -W","fromName":"René Scharfe.","fromEmail":"l.s.r@web.de","sentAt":"2021-02-15T18:47:48Z","receivedAt":"2021-02-15T18:50:00Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Am 15.02.21 um 16:50 schrieb Ævar Arnfjörð Bjarmason:\n> Fix what I believe to be a long-standing bug in how \"-W\" interacts\n> with displaying the hunk context on the @@ line: It should not be\n> displayed at all under -W.\n>\n> The long-standing semantics of how -W works and interacts with -U<n>\n> are rather easy to reason about:\n>\n>  * -W extends the context line up to the start of the function. With\n>     userdiff this means the language-aware regex rules in userdiff.c,\n>     or user-supplied rules.\n>\n>  * -U<n>, which defaults to -U3 shows at least <n> lines of context,\n>     if that's greater than what we'd extend the context to under -W\n>     then -U<n> wins.\n>\n>  * When showing the hunk context we look up from the first line we\n>    show of the diff, and find whatever looks like useful context above\n>    that line.\n>\n> Thus in e.g. the xdiff/xemit.c change being made in this commit we'll\n> correctly show \"xdl_emit_diff()\" in the hunk context under default\n> diff settings.\n>\n> But if we viewed it with the -W option we'd show \"is_empty_rec()\",\n> because we'd first find the \"xdl_emit_diff()\" context line, extend the\n> diff to that, and then would go look for context to show again.\n>\n> I don't think this behavior makes any sense, our context in this case\n> is what we're guaranteed to show as part of the diff itself.\n>\n> The user already asked us to find that context line and show it, we\n> don't need to then start showing the context above that line, which\n> they didn't ask for.\n\nHmm, that's subtle.\n\nYour reasoning applies to patches generated without -W as well.  If the\nprecontext contains a function line then the @@ line should not contain\na function comment.  However, e.g. with this:\n\n-- snip --\ncat >a <<EOF\nfunc a\n\nfunc b\n1\n2\n3\nEOF\nsed 's/3/three/' <a >b\ndiff -up a b\n-- snap --\n\n... I get this:\n\n--- a\t2021-02-15 18:30:21.000000000 +0100\n+++ b\t2021-02-15 18:30:21.000000000 +0100\n@@ -3,4 +3,4 @@ func a\n func b\n 1\n 2\n-3\n+three\n\nSo diff(1) shows the previous function line.  git diff does the same.\n\nThe behaviour of diff(1) and git diff does make sense to me: It's easy\nto implement and the only downside is that it produces extra output in\nsome cases.\n\nI can understand that users would rather have a tidy diff without\ndistractions, though.  So I like the output change you propose.\n\nHowever, I'm not sure it would be a good idea to clear @@ lines of hunks\ngenerated without -W that have function lines in their precontext, even\nthough it would be a logical thing to do.\n\n> This new behavior does give us the edge case that if we e.g. view the\n> diff here with \"-U150 -W\" we'd previously extend the context to the\n> middle of the \"is_func_rec()\" function, and show that function in the\n> hunk context. Now we'll show nothing.\n\nWell, the 150 lines of context are still shown (as they should be), but\nthe @@ line contains no function name anymore.\n\n> I think that change also makes sense. We're showing a change in the\n> \"xdl_emit_diff()\" function. That's our context for the change. It\n> doesn't make sense with -W to start fishing around for other\n> context.\n\nIt does make sense in the context of the diff(1) -p implementation, but\nyour change is consistent with the description of that option: \"Show\nwhich C function each change is in.\"\n\n> Arguably in that case we could save away the context we found in the\n> \"XDL_EMIT_FUNCCONTEXT\" in \"xdl_emit_diff()\" and show that if we end up\n> extending the diff past the function, either because of a high -U<n>\n> value, or because our change was right at the start.\n>\n> I wouldn't really mind if we did that, perhaps it would be a useful\n> marker with high -U<n> values to remind the user of what they're\n> looking at, but I also don't see the usefulness in practice, so let's\n> punt that for now.\n\nIt could be confusing for someone who expects the old behaviour, leaving\nit empty makes more sense to me.\n\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  Documentation/diff-options.txt | 4 ++++\n>  t/t4015-diff-whitespace.sh     | 2 +-\n>  t/t4018-diff-funcname.sh       | 7 +++++++\n>  xdiff/xemit.c                  | 4 +++-\n>  4 files changed, 15 insertions(+), 2 deletions(-)\n>\n> diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt\n> index e5733ccb2d..8ca59effa7 100644\n> --- a/Documentation/diff-options.txt\n> +++ b/Documentation/diff-options.txt\n> @@ -759,6 +759,10 @@ endif::git-format-patch[]\n>  \tThe function names are determined in the same way as\n>  \t`git diff` works out patch hunk headers (see 'Defining a\n>  \tcustom hunk-header' in linkgit:gitattributes[5]).\n> ++\n> +When showing the whole function for context the \"@@\" context line\n> +itself will always be empty, since the context that would otherwise be\n> +shown there will be the first line of the hunk being shown.\n>\n>  ifndef::git-format-patch[]\n>  ifndef::git-log[]\n> diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh\n> index 8c574221b2..0ffc845cdd 100755\n> --- a/t/t4015-diff-whitespace.sh\n> +++ b/t/t4015-diff-whitespace.sh\n> @@ -2133,7 +2133,7 @@ test_expect_success 'combine --ignore-blank-lines with --function-context 2' '\n>  \t\t--ignore-blank-lines --function-context a b >actual.raw &&\n>  \tsed -n \"/@@/,\\$p\" <actual.raw >actual &&\n>  \tcat <<-\\EOF >expect &&\n> -\t@@ -5,11 +6,9 @@ c\n> +\t@@ -5,11 +6,9 @@\n>  \t function\n>  \t 1\n>  \t 2\n> diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\n> index 80f35c5e16..f3374abd98 100755\n> --- a/t/t4018-diff-funcname.sh\n> +++ b/t/t4018-diff-funcname.sh\n> @@ -91,6 +91,13 @@ test_diff_funcname () {\n>  \t\tfi\n>  \t' &&\n>\n> +\ttest_expect_success \"$desc -W\" '\n> +\t\tgit diff -U0 -W \"$what\" >W-U0-diff &&\n> +\t\techo >W-U0-expected &&\n> +\t\tlast_diff_context_line W-U0-diff >W-U0-actual &&\n> +\t\ttest_cmp W-U0-expected W-U0-actual\n> +\t' &&\n> +\n>  \ttest_expect_success \"$desc (accumulated)\" '\n>  \t\tgit diff -U1 \"$what\".acc >diff &&\n>  \t\tlast_diff_context_line diff >actual.lines &&\n> diff --git a/xdiff/xemit.c b/xdiff/xemit.c\n> index 9d7d6c5087..02b5dbcc70 100644\n> --- a/xdiff/xemit.c\n> +++ b/xdiff/xemit.c\n> @@ -274,7 +274,9 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,\n>  \t\t */\n>\n>  \t\tif (xecfg->flags & XDL_EMIT_FUNCNAMES) {\n> -\t\t\tget_func_line(xe, xecfg, &func_line,\n> +\t\t\tget_func_line(xe, xecfg,\n> +\t\t\t\t      xecfg->flags & XDL_EMIT_FUNCCONTEXT\n> +\t\t\t\t      ? NULL : &func_line,\n\nWhy still search?  It would be better to turn off XDL_EMIT_FUNCNAMES if\nXDL_EMIT_FUNCCONTEXT is enabled -- a one-character change in diff.c.\n\n>  \t\t\t\t      s1 - 1, funclineprev);\n>  \t\t\tfunclineprev = s1 - 1;\n>  \t\t}\n>\n"},{"id":"416967","messageId":"87lfbpb1ei.fsf@evledraar.gmail.com","threadId":"55144","inReplyTo":"7984b3d6-7c01-565e-f5fd-3104af4b3ce6@kdbg.org","subject":"Re: [PATCH v2 19/27] gitattributes doc: document multi-line userdiff patterns","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T19:01:57Z","receivedAt":"2021-02-15T19:02:44Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, Feb 15 2021, Johannes Sixt wrote:\n\n> Am 15.02.21 um 16:44 schrieb Ævar Arnfjörð Bjarmason:\n>> diff --git a/t/t4018/custom.sh b/t/t4018/custom.sh\n>> index 72d38dad68..30df13d8b2 100755\n>> --- a/t/t4018/custom.sh\n>> +++ b/t/t4018/custom.sh\n>> @@ -111,3 +111,53 @@ ChangeMe\n>>     baz\n>>   EOF_TEST\n>> +\n>> +test_expect_success 'custom: setup negation syntax, ! is magic' '\n>> +\tgit config diff.custom.xfuncname \"!negation\n>> +line\"\n>> +'\n>> +\n>> +test_diff_funcname 'custom: negation syntax, ! is magic' \\\n>> +\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n>> +line\n>> +EOF_HUNK\n>> +line\n>> +!negation\n>> +\n>> +ChangeMe\n>> +\n>> +baz\n>> +EOF_TEST\n>> +\n>> +test_expect_success 'custom: setup negation syntax, use [!] to override ! magic' '\n>> +\tgit config diff.custom.xfuncname \"[!]negation\n>> +line\"\n>> +'\n>> +\n>> +test_diff_funcname 'custom: negation syntax, use [!] to override ! magic' \\\n>> +\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n>> +!negation\n>> +EOF_HUNK\n>> +line\n>> +!negation\n>> +\n>> +ChangeMe\n>> +\n>> +baz\n>> +EOF_TEST\n>> +\n>> +test_expect_success 'custom: setup captures in multiple patterns' '\n>> +\tgit config diff.custom.xfuncname \"!^=head\n>> +^format ([^ ]+)\n>> +^sub ([^;]+)\"\n>> +'\n>> +\n>> +test_diff_funcname 'custom: captures in multiple patterns' \\\n>> +\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n>> +foo\n>> +EOF_HUNK\n>> +sub foo;\n>> +=head1\n>> +ChangeMe\n>> +\n>> +EOF_TEST\n>\n> This test would not catch a regression. You must leave a line between\n> the candidate-that-must-not-be, =head1, and ChangeMe, otherwise,\n> =head1 is never tested against the negation pattern.\n>\n> Or did you change the diff invocation in an earlier patch such that it\n> does not emit context lines?\n>\n>> diff --git a/t/t4018/perl.sh b/t/t4018/perl.sh\n>> index ac8fff7417..2952483a2c 100755\n>> --- a/t/t4018/perl.sh\n>> +++ b/t/t4018/perl.sh\n>> @@ -76,3 +76,19 @@ sub RIGHT\n>>   \tprint \"ChangeMe\\n\";\n>>   }\n>>   EOF_TEST\n>> +\n>> +\n>> +test_expect_success 'custom: setup config overrides built-in patterns' '\n>> +\tgit config diff.perl.xfuncname \"!^=head\n>> +^[^ ]+.*\"\n>> +'\n>> +\n>> +test_diff_funcname 'custom: config overrides built-in patterns' \\\n>> +\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n>> +sub foo;\n>> +EOF_HUNK\n>> +sub foo;\n>> +=head1\n>> +ChangeMe\n>> +\n>> +EOF_TEST\n>\n> Same here.\n\nWell spotted. Both of these test work for their advertised\npurpose. I.e. \"we can override the perl built-in config\" and \"capture\ncount gets reset\", but you're right that the =head1 negation rule is\nredundant at this point in the series.\n\nIn 23/27 we start testing with -U0, at which point it starts\nworking. But of course a better fix is to just add an empty line here...\n"},{"id":"416969","messageId":"d0dfc18a-1f22-f4f1-bf99-6dc829f3cf04@kdbg.org","threadId":"55144","inReplyTo":"20210215154427.32693-24-avarab@gmail.com","subject":"Re: [PATCH v2 23/27] userdiff tests: test hunk header selection with -U0","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2021-02-15T19:09:29Z","receivedAt":"2021-02-15T19:10:05Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 15.02.21 um 16:44 schrieb Ævar Arnfjörð Bjarmason:\n> The userdiff tests have used a custom -U1 context since\n> f12c66b9bb (userdiff/perl: anchor \"sub\" and \"package\" patterns on the\n> left, 2011-05-21). Changing it to -U0 doesn't change the results for\n> any of the tests, except one.\n> \n> Let's test for this case explicitly. I.e. that we go \"beyond\" the\n> selected context to find our hunk header. In many cases the desired\n> hunk header is part of the diff itself under -U1.\n\nIs this intended as a sanity check of test cases? (Otherwise, I see only \nthat we are running two diffs instead of just one against every test \ncase, but not which problem this is solving.)\n\n> \n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>   t/t4018-diff-funcname.sh | 13 +++++++++++++\n>   t/t4018/custom.sh        |  1 +\n>   2 files changed, 14 insertions(+)\n> \n> diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\n> index 8b4500037f..d41aed9ba2 100755\n> --- a/t/t4018-diff-funcname.sh\n> +++ b/t/t4018-diff-funcname.sh\n> @@ -57,6 +57,7 @@ test_diff_funcname () {\n>   \tcat <&8 >arg.header &&\n>   \tcat <&9 >arg.test &&\n>   \twhat=$(cat arg.what) &&\n> +\targ_diff_U0=$2 &&\n>   \n>   \ttest_expect_success \"setup: $desc\" '\n>   \t\tcp arg.test \"$what\" &&\n> @@ -78,6 +79,18 @@ test_diff_funcname () {\n>   \t\ttest_cmp expected actual\n>   \t' &&\n>   \n> +\ttest_expect_success \"$desc -U0\" '\n> +\t\tgit diff -U0 \"$what\" >diff &&\n> +\t\tlast_diff_context_line diff >actual &&\n> +\t\tif test -n \"$arg_diff_U0\"\n> +\t\tthen\n> +\t\t\techo \"$arg_diff_U0\" >new-expected &&\n> +\t\t\ttest_cmp new-expected actual\n> +\t\telse\n> +\t\t\ttest_cmp expected actual\n> +\t\tfi\n> +\t' &&\n> +\n>   \ttest_expect_success \"$desc (accumulated)\" '\n>   \t\tgit diff -U1 \"$what\".acc >diff &&\n>   \t\tlast_diff_context_line diff >actual.lines &&\n> diff --git a/t/t4018/custom.sh b/t/t4018/custom.sh\n> index 886de9cddb..a090f7bfc2 100755\n> --- a/t/t4018/custom.sh\n> +++ b/t/t4018/custom.sh\n> @@ -10,6 +10,7 @@ test_expect_success 'custom: setup non-trivial custom' '\n>   '\n>   \n>   test_diff_funcname 'custom: non-trivial custom pattern' \\\n> +\t'System.out.print(x + \" bottles of beer on the wall \"' \\\n>   \t8<<\\EOF_HUNK 9<<\\EOF_TEST\n>   int special, RIGHT;\n>   EOF_HUNK\n> \n\n-- Hannes\n"},{"id":"416970","messageId":"87im6tb0ca.fsf@evledraar.gmail.com","threadId":"55144","inReplyTo":"d02c99b8-ae26-9804-480c-eae880f4a6cb@web.de","subject":"Re: [PATCH 1/2] diff: do not display hunk context under -W","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T19:24:53Z","receivedAt":"2021-02-15T19:25:51Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, Feb 15 2021, René Scharfe. wrote:\n\n> Am 15.02.21 um 16:50 schrieb Ævar Arnfjörð Bjarmason:\n>> Fix what I believe to be a long-standing bug in how \"-W\" interacts\n>> with displaying the hunk context on the @@ line: It should not be\n>> displayed at all under -W.\n>>\n>> The long-standing semantics of how -W works and interacts with -U<n>\n>> are rather easy to reason about:\n>>\n>>  * -W extends the context line up to the start of the function. With\n>>     userdiff this means the language-aware regex rules in userdiff.c,\n>>     or user-supplied rules.\n>>\n>>  * -U<n>, which defaults to -U3 shows at least <n> lines of context,\n>>     if that's greater than what we'd extend the context to under -W\n>>     then -U<n> wins.\n>>\n>>  * When showing the hunk context we look up from the first line we\n>>    show of the diff, and find whatever looks like useful context above\n>>    that line.\n>>\n>> Thus in e.g. the xdiff/xemit.c change being made in this commit we'll\n>> correctly show \"xdl_emit_diff()\" in the hunk context under default\n>> diff settings.\n>>\n>> But if we viewed it with the -W option we'd show \"is_empty_rec()\",\n>> because we'd first find the \"xdl_emit_diff()\" context line, extend the\n>> diff to that, and then would go look for context to show again.\n>>\n>> I don't think this behavior makes any sense, our context in this case\n>> is what we're guaranteed to show as part of the diff itself.\n>>\n>> The user already asked us to find that context line and show it, we\n>> don't need to then start showing the context above that line, which\n>> they didn't ask for.\n>\n> Hmm, that's subtle.\n>\n> Your reasoning applies to patches generated without -W as well.  If the\n> precontext contains a function line then the @@ line should not contain\n> a function comment.  However, e.g. with this:\n>\n> -- snip --\n> cat >a <<EOF\n> func a\n>\n> func b\n> 1\n> 2\n> 3\n> EOF\n> sed 's/3/three/' <a >b\n> diff -up a b\n> -- snap --\n>\n> ... I get this:\n>\n> --- a\t2021-02-15 18:30:21.000000000 +0100\n> +++ b\t2021-02-15 18:30:21.000000000 +0100\n> @@ -3,4 +3,4 @@ func a\n>  func b\n>  1\n>  2\n> -3\n> +three\n>\n> So diff(1) shows the previous function line.  git diff does the same.\n>\n> The behaviour of diff(1) and git diff does make sense to me: It's easy\n> to implement and the only downside is that it produces extra output in\n> some cases.\n>\n> I can understand that users would rather have a tidy diff without\n> distractions, though.  So I like the output change you propose.\n\nDoes GNU diff have something like git's -W, both \"diff -U 0 -F func a b\"\nand \"diff -U 0 -p a b\" don't extend the context window as we do.\n\nI don't think the patch I'm submitting here would make sense for GNU\ndiff, since there it just shows the context without being guaranteed to\nshow the full set of lines leading up to it under -W, but with Git diff\nwe do that, so I think it makes sense to omit the context.\n\n> However, I'm not sure it would be a good idea to clear @@ lines of hunks\n> generated without -W that have function lines in their precontext, even\n> though it would be a logical thing to do.\n\nYes, I don't think that's a good idea either. I think it only makes\nsense under -W where the user explicitly asks \"show me the function this\nchange was in\", and we're (before this patch) showing different context\non the basis of emergent behavior.\n\n>> This new behavior does give us the edge case that if we e.g. view the\n>> diff here with \"-U150 -W\" we'd previously extend the context to the\n>> middle of the \"is_func_rec()\" function, and show that function in the\n>> hunk context. Now we'll show nothing.\n>\n> Well, the 150 lines of context are still shown (as they should be), but\n> the @@ line contains no function name anymore.\n\nYes, indeed. I'll reword that to \"now we'll show no context in that\ncase\" or something...\n\n>> I think that change also makes sense. We're showing a change in the\n>> \"xdl_emit_diff()\" function. That's our context for the change. It\n>> doesn't make sense with -W to start fishing around for other\n>> context.\n>\n> It does make sense in the context of the diff(1) -p implementation, but\n> your change is consistent with the description of that option: \"Show\n> which C function each change is in.\"\n\nI hadn't spotted that, we just said:\n\n    Show whole function as context lines for each change. The function\n    names are determined in the same way as git diff works out patch\n    hunk headers\n\nWhich I think can more obviously be read as the existing behavior being\ndesired, and this patch being a change to documented behavior.\n\n(I think it is, I just think it makes sense to change the docs &\nbehavior int this case)\n\n>> Arguably in that case we could save away the context we found in the\n>> \"XDL_EMIT_FUNCCONTEXT\" in \"xdl_emit_diff()\" and show that if we end up\n>> extending the diff past the function, either because of a high -U<n>\n>> value, or because our change was right at the start.\n>>\n>> I wouldn't really mind if we did that, perhaps it would be a useful\n>> marker with high -U<n> values to remind the user of what they're\n>> looking at, but I also don't see the usefulness in practice, so let's\n>> punt that for now.\n>\n> It could be confusing for someone who expects the old behaviour, leaving\n> it empty makes more sense to me.\n\nFWIW it would be useful for:\n\n    git log -U1000 -W\n\nAnd then searching for \"@@\" in the pager to find changes to specific\nfunctions.\n\n>>\n>> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>> ---\n>>  Documentation/diff-options.txt | 4 ++++\n>>  t/t4015-diff-whitespace.sh     | 2 +-\n>>  t/t4018-diff-funcname.sh       | 7 +++++++\n>>  xdiff/xemit.c                  | 4 +++-\n>>  4 files changed, 15 insertions(+), 2 deletions(-)\n>>\n>> diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt\n>> index e5733ccb2d..8ca59effa7 100644\n>> --- a/Documentation/diff-options.txt\n>> +++ b/Documentation/diff-options.txt\n>> @@ -759,6 +759,10 @@ endif::git-format-patch[]\n>>  \tThe function names are determined in the same way as\n>>  \t`git diff` works out patch hunk headers (see 'Defining a\n>>  \tcustom hunk-header' in linkgit:gitattributes[5]).\n>> ++\n>> +When showing the whole function for context the \"@@\" context line\n>> +itself will always be empty, since the context that would otherwise be\n>> +shown there will be the first line of the hunk being shown.\n>>\n>>  ifndef::git-format-patch[]\n>>  ifndef::git-log[]\n>> diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh\n>> index 8c574221b2..0ffc845cdd 100755\n>> --- a/t/t4015-diff-whitespace.sh\n>> +++ b/t/t4015-diff-whitespace.sh\n>> @@ -2133,7 +2133,7 @@ test_expect_success 'combine --ignore-blank-lines with --function-context 2' '\n>>  \t\t--ignore-blank-lines --function-context a b >actual.raw &&\n>>  \tsed -n \"/@@/,\\$p\" <actual.raw >actual &&\n>>  \tcat <<-\\EOF >expect &&\n>> -\t@@ -5,11 +6,9 @@ c\n>> +\t@@ -5,11 +6,9 @@\n>>  \t function\n>>  \t 1\n>>  \t 2\n>> diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\n>> index 80f35c5e16..f3374abd98 100755\n>> --- a/t/t4018-diff-funcname.sh\n>> +++ b/t/t4018-diff-funcname.sh\n>> @@ -91,6 +91,13 @@ test_diff_funcname () {\n>>  \t\tfi\n>>  \t' &&\n>>\n>> +\ttest_expect_success \"$desc -W\" '\n>> +\t\tgit diff -U0 -W \"$what\" >W-U0-diff &&\n>> +\t\techo >W-U0-expected &&\n>> +\t\tlast_diff_context_line W-U0-diff >W-U0-actual &&\n>> +\t\ttest_cmp W-U0-expected W-U0-actual\n>> +\t' &&\n>> +\n>>  \ttest_expect_success \"$desc (accumulated)\" '\n>>  \t\tgit diff -U1 \"$what\".acc >diff &&\n>>  \t\tlast_diff_context_line diff >actual.lines &&\n>> diff --git a/xdiff/xemit.c b/xdiff/xemit.c\n>> index 9d7d6c5087..02b5dbcc70 100644\n>> --- a/xdiff/xemit.c\n>> +++ b/xdiff/xemit.c\n>> @@ -274,7 +274,9 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,\n>>  \t\t */\n>>\n>>  \t\tif (xecfg->flags & XDL_EMIT_FUNCNAMES) {\n>> -\t\t\tget_func_line(xe, xecfg, &func_line,\n>> +\t\t\tget_func_line(xe, xecfg,\n>> +\t\t\t\t      xecfg->flags & XDL_EMIT_FUNCCONTEXT\n>> +\t\t\t\t      ? NULL : &func_line,\n>\n> Why still search?  It would be better to turn off XDL_EMIT_FUNCNAMES if\n> XDL_EMIT_FUNCCONTEXT is enabled -- a one-character change in diff.c.\n\nI just didn't read the diff/xdiff code carefully enough. Will fix.\n\n>>  \t\t\t\t      s1 - 1, funclineprev);\n>>  \t\t\tfunclineprev = s1 - 1;\n>>  \t\t}\n>>\n\n"},{"id":"416972","messageId":"177512df-77a1-6d3a-929f-4422665405a8@kdbg.org","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"Re: [PATCH v2 00/27] userdiff: refactor + test + doc + misc improvements","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2021-02-15T20:03:11Z","receivedAt":"2021-02-15T20:03:34Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 15.02.21 um 16:44 schrieb Ævar Arnfjörð Bjarmason:\n> Incorporates all the feedback on v2 and more, see the range-diff\n> below.\n\nI've read through all patches and left a few comments.\n\nMy main critique is that the new way to specify test cases is not an \nimprovement because the code is hard to parse. The  desire to improve \ntest precision moves the balance too far away from the simplicity to add \nnew test cases, IMO.\n\nI appreciate the new test cases for ADA and Ruby as well as the \ndiscovery and fixes of the many small deficiencies.\n\n-- Hannes\n"},{"id":"416974","messageId":"87ft1xayee.fsf@evledraar.gmail.com","threadId":"55144","inReplyTo":"0f70d5e4-cf35-1e67-be02-2baf3036d471@kdbg.org","subject":"Re: [PATCH v2 12/27] userdiff tests: rewrite hunk header test infrastructure","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-15T20:06:49Z","receivedAt":"2021-02-15T20:07:35Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, Feb 15 2021, Johannes Sixt wrote:\n\n> Am 15.02.21 um 16:44 schrieb Ævar Arnfjörð Bjarmason:\n>> Rewrite the hunk header test infrastructure introduced in\n>> bfa7d01413 (t4018: an infrastructure to test hunk headers,\n>> 2014-03-21). See c228a5c077 (Merge branch 'js/userdiff-cc',\n>> 2014-03-31) for the whole series that commit was part of.\n>> As noted in an earlier commit that change introduced the regression\n>> of\n>> not testing for the full hunk line, but just whether \"RIGHT\" appeared\n>> on it[1]. A preceding commit fixed that specific issue, but we were\n>> still left with the inflexibility of the approach described in the\n>> now-deleted t/t4018/README.\n>> I.e. to add any sort of new tests that used the existing test data\n>> we'd either need to add more files like the recently added (but now\n>> deleted) *.ctx) files, using the filesystem as our test datastructure,\n>> or introduce more parsing for the custom file format we were growing\n>> here.\n>> Let's instead just move this over to using a custom test\n>> function. This makes it trivial to add new tests by adding new\n>> optional parameters to the function. Let's still keep the relevant\n>> files in the \"t/t4018/\" subdirectory instead of adding ~1.5k\n>> lines (and growing) to \"t/t4018-diff-funcname.sh\"\n>> If this diff is viewed with \"--color-moved=plain\" we can see that\n>> there's no changes to the lines being moved into the new *.sh files,\n>> i.e. all the deletions are moves. I'm just adding boilerplate around\n>> those existing lines.\n>> The one-off refactoring was performed by an ad-hoc shellscript [2].\n>> 1. https://lore.kernel.org/git/87wnvbbf2y.fsf@evledraar.gmail.com/\n>> 2.\n>> \t#!/bin/sh\n>> \tset -ex\n>> \tgit rm README*\n>> \tfor t in $(git ls-files ':!*.ctx')\n>> \tdo\n>> \t\tlang=$(echo $t | sed 's/-.*//')\n>> \t\tdesc=$(echo $t | sed -E 's/^[^-]*-//' | tr - \" \")\n>> \t\tif ! test -e $lang.sh\n>> \t\tthen\n>> \t\t\tcat >$lang.sh <<-EOF\n>> \t\t\t#!/bin/sh\n>> \t\t\t#\n>> \t\t\t# See ../t4018-diff-funcname.sh's test_diff_funcname()\n>> \t\t\t#\n>> \t\t\tEOF\n>> \t\telse\n>> \t\t\techo >>$lang.sh\n>> \t        fi\n>> \t\t(\n>> \t            printf \"test_diff_funcname '%s: %s' \\\\\" \"$lang\" \"$desc\"\n>> \t            echo\n>> \t            printf \"\\t8<<%sEOF_HUNK 9<<%sEOF_TEST\\n\" '\\' '\\'\n>> \t            cat $t.ctx\n>> \t            printf \"EOF_HUNK\\n\"\n>> \t            cat $t\n>> \t            printf \"EOF_TEST\\n\"\n>> \t\t) >>$lang.sh\n>> \t\tchmod +x $lang.sh\n>> \t\tgit add $lang.sh\n>> \t        git rm $t $t.ctx\n>> \tdone\n>> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>> ---\n>\n>> diff --git a/t/t4018/bash.sh b/t/t4018/bash.sh\n>> new file mode 100755\n>> index 0000000000..69144d9144\n>> --- /dev/null\n>> +++ b/t/t4018/bash.sh\n>> @@ -0,0 +1,160 @@\n>> +#!/bin/sh\n>> +#\n>> +# See ../t4018-diff-funcname.sh's test_diff_funcname()\n>> +#\n>> +\n>> +test_diff_funcname 'bash: arithmetic function' \\\n>> +\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n>> +RIGHT()\n>> +EOF_HUNK\n>> +RIGHT() ((\n>> +\n>> +    ChangeMe = \"$x\" + \"$y\"\n>> +))\n>> +EOF_TEST\n>> +\n>> +test_diff_funcname 'bash: bashism style compact' \\\n>> +\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n>> +function RIGHT {\n>> +EOF_HUNK\n>> +function RIGHT {\n>> +    function InvalidSyntax{\n>> +        :\n>> +        echo 'ChangeMe'\n>> +    }\n>> +}\n>> +EOF_TEST\n>> +\n>> +test_diff_funcname 'bash: bashism style function' \\\n>> +\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n>> +function RIGHT {\n>> +EOF_HUNK\n>> +function RIGHT {\n>> +    :\n>> +    echo 'ChangeMe'\n>> +}\n>> +EOF_TEST\n>> [...]\n>\n> That is not my dream of \"simple\". But I'm not a userdiff author\n> anymore, so...\n>\n> I don't know, yet, where this is heading to what the advantage is. At\n> any rate,[...]\n\nI originally started writing this because I noticed I could break the\nuserdiff.c patterns and still have all tests pass, i.e. if you screw up\nthe capture grouping you can go from:\n    \n    @@ -2,3 +2,3 @@ function        RIGHT   (       )       {\n    @@ -2,3 +2,3 @@ RIGHT   (       )\n\nto:\n    \n    @@ -2,3 +2,3 @@ function        RIGHT   (       )       {\n    @@ -2,3 +2,3 @@          RIGHT  (       )\n    \nAnd we wouldn't care because we just \"grep 'RIGHT'\". In this case we\nreally care about the difference between \"^[ \\t]*(.*)$\" and a broken\n\"^([ \\t]*.*)$\" so not having the tests structurally hide the difference\nmakes sense.\n\n> [...] \"trivial to add new tests\" was also the case when each test case\n> was in its own file[...]\n\n\"trivial to add new tests by adding new optional parameters to the\nfunction\". I.e. aside from the s/grep/test_cmp/ change in 09/27 the\nexisting tests were OK if you wanted to test exactly what they expected,\nand no more.\n\nI think it just makes sense to have a test helper function instead and\nlittle bit of boilerplate, as seen e.g. in 14/27 and later in the series\nwe can add new test modes and set per-test config without needing the\ntop-level dispatch loop to be aware of it.\n\n> [...] Without the boilerplate!\n\nI realize that's a matter of taste, i.e. when to come up with some\ncustom format v.s. writng a function.\n\nFWIW as someone who didn't author the format I've come across it N times\nover the years and each time ended up being more confused than when\nreading any custom test function we have.\n\nFor those you can usually just look at the definition/arguments, whereas\nthis always required a careful read of t4018-diff-funcname.sh.\n\nI also find it easier to have one ~160 line file in my editor than ~150\nlines spread over 15 files, as in the recent addition of bash support in\n2ff6c34612 (userdiff: support Bash, 2020-10-22).\n\nIt also depends on how you're counting boilerplate, if you're looking at\nit as a patch on the ML it would be ~160 lines of bash.sh, v.s. ~150\nlines of the same content, if we're counting the boilerplate diff of 6\nlines for every new file :)\n\n\n"},{"id":"416975","messageId":"68c44555-7469-2afc-a0cf-50e8368a31f9@kdbg.org","threadId":"55144","inReplyTo":"87ft1xayee.fsf@evledraar.gmail.com","subject":"Re: [PATCH v2 12/27] userdiff tests: rewrite hunk header test infrastructure","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2021-02-15T20:29:41Z","receivedAt":"2021-02-15T20:30:08Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 15.02.21 um 21:06 schrieb Ævar Arnfjörð Bjarmason:\n> On Mon, Feb 15 2021, Johannes Sixt wrote:\n>> Am 15.02.21 um 16:44 schrieb Ævar Arnfjörð Bjarmason:\n>>> +test_diff_funcname 'bash: bashism style compact' \\\n>>> +\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n>>> +function RIGHT {\n>>> +EOF_HUNK\n>>> +function RIGHT {\n>>> +    function InvalidSyntax{\n>>> +        :\n>>> +        echo 'ChangeMe'\n>>> +    }\n>>> +}\n>>> +EOF_TEST\n>>> +\n>>> +test_diff_funcname 'bash: bashism style function' \\\n>>> +\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n>>> +function RIGHT {\n>>> +EOF_HUNK\n>>> +function RIGHT {\n>>> +    :\n>>> +    echo 'ChangeMe'\n>>> +}\n>>> +EOF_TEST\n>>> [...]\n>>\n>> That is not my dream of \"simple\". But I'm not a userdiff author\n>> anymore, so...\n>>\n>> I don't know, yet, where this is heading to what the advantage is. At\n>> any rate,[...]\n> \n> I originally started writing this because I noticed I could break the\n> userdiff.c patterns and still have all tests pass, i.e. if you screw up\n> the capture grouping you can go from:\n>      \n>      @@ -2,3 +2,3 @@ function        RIGHT   (       )       {\n>      @@ -2,3 +2,3 @@ RIGHT   (       )\n> \n> to:\n>      \n>      @@ -2,3 +2,3 @@ function        RIGHT   (       )       {\n>      @@ -2,3 +2,3 @@          RIGHT  (       )\n>      \n> And we wouldn't care because we just \"grep 'RIGHT'\". In this case we\n> really care about the difference between \"^[ \\t]*(.*)$\" and a broken\n> \"^([ \\t]*.*)$\" so not having the tests structurally hide the difference\n> makes sense.\n\nBut why care? The second version is not wrong. If it's not pretty, the \npattern author will notice soon enough. I regard avoiding the regression \nyou cite less important than the simplicity of adding new test cases.\n\n> \n>> [...] \"trivial to add new tests\" was also the case when each test case\n>> was in its own file[...]\n> \n> \"trivial to add new tests by adding new optional parameters to the\n> function\". I.e. aside from the s/grep/test_cmp/ change in 09/27 the\n> existing tests were OK if you wanted to test exactly what they expected,\n> and no more.\n\nI did not understand what you meant with \"new optional parameters\" and I \nstill do not and why it is desirable.\n\n> \n> I think it just makes sense to have a test helper function instead and\n> little bit of boilerplate, as seen e.g. in 14/27 and later in the series\n> we can add new test modes and set per-test config without needing the\n> top-level dispatch loop to be aware of it.\n> \n>> [...] Without the boilerplate!\n> \n> I realize that's a matter of taste, i.e. when to come up with some\n> custom format v.s. writng a function.\n> \n> FWIW as someone who didn't author the format I've come across it N times\n> over the years and each time ended up being more confused than when\n> reading any custom test function we have.\n> \n> For those you can usually just look at the definition/arguments, whereas\n> this always required a careful read of t4018-diff-funcname.sh.\n> \n> I also find it easier to have one ~160 line file in my editor than ~150\n> lines spread over 15 files, as in the recent addition of bash support in\n> 2ff6c34612 (userdiff: support Bash, 2020-10-22).\n\nI wouldn't mind having all test cases in a single file. But then it \nshould be one file in the language that is being tested, not shell \nscript with foreign text between the lines. Adding test cases should be \neasy for authors; they should not have to be proficient in shell \nscripting (and knwo about 130 lines of CodingGuidelines).\n\n-- Hannes\n"},{"id":"416982","messageId":"6ccba73c-ea51-7842-8cc3-d96fd71c0efc@web.de","threadId":"55144","inReplyTo":"87im6tb0ca.fsf@evledraar.gmail.com","subject":"Re: [PATCH 1/2] diff: do not display hunk context under -W","fromName":"René Scharfe.","fromEmail":"l.s.r@web.de","sentAt":"2021-02-15T21:17:47Z","receivedAt":"2021-02-15T21:19:55Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Am 15.02.21 um 20:24 schrieb Ævar Arnfjörð Bjarmason:\n>\n> On Mon, Feb 15 2021, René Scharfe. wrote:\n>\n>> Your reasoning applies to patches generated without -W as well.  If the\n>> precontext contains a function line then the @@ line should not contain\n>> a function comment.  However, e.g. with this:\n>>\n>> -- snip --\n>> cat >a <<EOF\n>> func a\n>>\n>> func b\n>> 1\n>> 2\n>> 3\n>> EOF\n>> sed 's/3/three/' <a >b\n>> diff -up a b\n>> -- snap --\n>>\n>> ... I get this:\n>>\n>> --- a\t2021-02-15 18:30:21.000000000 +0100\n>> +++ b\t2021-02-15 18:30:21.000000000 +0100\n>> @@ -3,4 +3,4 @@ func a\n>>  func b\n>>  1\n>>  2\n>> -3\n>> +three\n>>\n>> So diff(1) shows the previous function line.  git diff does the same.\n>>\n>> The behaviour of diff(1) and git diff does make sense to me: It's easy\n>> to implement and the only downside is that it produces extra output in\n>> some cases.\n>>\n>> I can understand that users would rather have a tidy diff without\n>> distractions, though.  So I like the output change you propose.\n>\n> Does GNU diff have something like git's -W, both \"diff -U 0 -F func a b\"\n> and \"diff -U 0 -p a b\" don't extend the context window as we do.\n\nExactly my point: GNU diff doesn't have something like -W, but still can\nshow an unchanged function at the @@ line, because it searches upwards\nstarting just above the first shown line, and the name of the actually\nchanged function might be in one of the shown lines.\n\n> I don't think the patch I'm submitting here would make sense for GNU\n> diff, since there it just shows the context without being guaranteed to\n> show the full set of lines leading up to it under -W, but with Git diff\n> we do that, so I think it makes sense to omit the context.\n\nGiven the goal to show the name of the changed function it *would* make\nsense to start searching at the first changed line instead.\n\n>> However, I'm not sure it would be a good idea to clear @@ lines of hunks\n>> generated without -W that have function lines in their precontext, even\n>> though it would be a logical thing to do.\n>\n> Yes, I don't think that's a good idea either. I think it only makes\n> sense under -W where the user explicitly asks \"show me the function this\n> change was in\", and we're (before this patch) showing different context\n> on the basis of emergent behavior.\n\nRight, -W never needs diff(1)'s -p, while the case is less clear for\ndiffs generated without -W.\n\nRené\n"},{"id":"416995","messageId":"xmqq7dn8u7dz.fsf@gitster.c.googlers.com","threadId":"55144","inReplyTo":"20210215155020.2804-2-avarab@gmail.com","subject":"Re: [PATCH 1/2] diff: do not display hunk context under -W","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-02-16T01:30:00Z","receivedAt":"2021-02-16T01:31:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> The long-standing semantics of how -W works and interacts with -U<n>\n> are rather easy to reason about:\n>\n>  * -W extends the context line up to the start of the function. With\n>     userdiff this means the language-aware regex rules in userdiff.c,\n>     or user-supplied rules.\n\nOK.\n\n>  * -U<n>, which defaults to -U3 shows at least <n> lines of context,\n>     if that's greater than what we'd extend the context to under -W\n>     then -U<n> wins.\n\nSorry, but I cannot quite guess what you mean here.  Do you mean:\n\n    We first go back <n> lines due to -U<n>.  If we have not found a\n    function header line that comes before the first changed line\n    within that <n> lines, we keep going back to satisfy -W (i.e. -W\n    \"wins\" if \"-U<n>\" is small).  On the other hand, after going\n    back <n> lines, if we have already seen a function header before\n    the first changed line within these <n> lines, there is no need\n    to go back further to satisfy -W (i.e. -U<n> \"wins\" and makes -W\n    irrelevant).\n\nIf that is the case, I think I understand it.\n\n>  * When showing the hunk context we look up from the first line we\n>    show of the diff, and find whatever looks like useful context above\n>    that line.\n\nYes.\n\n> Thus in e.g. the xdiff/xemit.c change being made in this commit we'll\n> correctly show \"xdl_emit_diff()\" in the hunk context under default\n> diff settings.\n>\n> But if we viewed it with the -W option we'd show \"is_empty_rec()\",\n> because we'd first find the \"xdl_emit_diff()\" context line, extend the\n> diff to that, and then would go look for context to show again.\n\nMakes sense.  As long as we apply this when \"-W wins\", i.e we extend\nthe precontext so that the hunk begins on the line that match the\nfunction header pattern, there isn't much point in showing only the\nname of the function that comes before this hunk.\n\nOn the other hand, if \"-U<n> won\", i.e. the first precontext line\nthat is shown in the hunk is _before_ the line -W chose to extend\nthe hunk to (in this case, where xdl_emit_diff begins) because -U<n>\ngave a large enough number, I do want to see the name of the fuction\nI am looking at the tail of in the precontext on the hunk header\nline.  E.g. \"git diff -W -U120 xdiff/xemit.c\" on this patch should\nshow is_empty_rec() as the hunk header.\n\n> This new behavior does give us the edge case that if we e.g. view the\n> diff here with \"-U150 -W\" we'd previously extend the context to the\n> middle of the \"is_func_rec()\" function, and show that function in the\n> hunk context. Now we'll show nothing.\n\nTo me, that sounds like a grave regression.  Why lose the\ninformation?\n\nThis may be coming from the difference between us, i.e. I read a lot\nmore patches written by other people than my own changes written for\nmy next commit, so every bit of hint helps, and the name of the\nfunction I am seeing its latter half in the precontext is sometimes\na useful thing to see.\n"},{"id":"416996","messageId":"xmqq35xwu71w.fsf@gitster.c.googlers.com","threadId":"55144","inReplyTo":"xmqq7dn8u7dz.fsf@gitster.c.googlers.com","subject":"Re: [PATCH 1/2] diff: do not display hunk context under -W","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-02-16T01:37:15Z","receivedAt":"2021-02-16T01:39:38Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>\n>> But if we viewed it with the -W option we'd show \"is_empty_rec()\",\n>> because we'd first find the \"xdl_emit_diff()\" context line, extend the\n>> diff to that, and then would go look for context to show again.\n>\n> Makes sense.\n\nEhh, I did not mean \"the current behaviour with -W that shows\nis_empty_rec makes sense\".  The observation you made (which lead to\nthe conclusion in your next paragraph that it is not a good idea\nto show is_empty_rec on the hunk header line) made sense to me.\n\nBut I do not think any change from the current behaviour should be\nmade if -U<n> wins -W (i.e. the first precontext line shown in the\nhunk header is not due to -W).  We should hunt for the name of the\nfunction whose latter half we are seeing at the beginning of the\nhunk in that case.\n\nThanks.\n"},{"id":"417009","messageId":"10e6cd64-8c1c-20ea-154e-89fad6664a5e@kdbg.org","threadId":"55144","inReplyTo":"xmqq7dn8u7dz.fsf@gitster.c.googlers.com","subject":"Re: [PATCH 1/2] diff: do not display hunk context under -W","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2021-02-16T07:20:29Z","receivedAt":"2021-02-16T07:21:17Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 16.02.21 um 02:30 schrieb Junio C Hamano:\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>> This new behavior does give us the edge case that if we e.g. view the\n>> diff here with \"-U150 -W\" we'd previously extend the context to the\n>> middle of the \"is_func_rec()\" function, and show that function in the\n>> hunk context. Now we'll show nothing.\n> \n> To me, that sounds like a grave regression.  Why lose the\n> information?\n> \n> This may be coming from the difference between us, i.e. I read a lot\n> more patches written by other people than my own changes written for\n> my next commit, so every bit of hint helps, and the name of the\n> function I am seeing its latter half in the precontext is sometimes\n> a useful thing to see.\n\nI totally agree with your assessment. I wouldn't even have removed the \nhunk header in the case of \"-W wins\", either, but that is a case that I \ncan live with when others think it makes sense.\n\n-- Hannes\n"},{"id":"417010","messageId":"e8cd8834-0d98-9b2f-51f5-ae4f9296b9d2@kdbg.org","threadId":"55144","inReplyTo":"20210215155020.2804-3-avarab@gmail.com","subject":"Re: [PATCH 2/2] diff: test and document -W interaction with -U<n>","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2021-02-16T07:26:29Z","receivedAt":"2021-02-16T07:26:53Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 15.02.21 um 16:50 schrieb Ævar Arnfjörð Bjarmason:\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>   Documentation/diff-options.txt | 8 ++++++++\n>   t/t4018-diff-funcname.sh       | 5 +++++\n>   2 files changed, 13 insertions(+)\n> \n> diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt\n> index 8ca59effa7..3c19c78616 100644\n> --- a/Documentation/diff-options.txt\n> +++ b/Documentation/diff-options.txt\n> @@ -88,6 +88,11 @@ endif::git-log[]\n>   --unified=<n>::\n>   \tGenerate diffs with <n> lines of context instead of\n>   \tthe usual three.\n> ++\n> +Under `-W` generates diffs with at least <n> lines of context, if the\n> +number is lower than the context `-U<n>` would extend the diff to then\n> +`-U<n>` takes precedence.\n> +\n\nHow about (not as separate paragraph):\n\nWhen combined with `--function-context`, this specifies the minimum of \ncontext lines.\n\n>   ifndef::git-format-patch[]\n>   \tImplies `--patch`.\n>   endif::git-format-patch[]\n> @@ -763,6 +768,9 @@ endif::git-format-patch[]\n>   When showing the whole function for context the \"@@\" context line\n>   itself will always be empty, since the context that would otherwise be\n>   shown there will be the first line of the hunk being shown.\n> ++\n> +See the documentation for `-U<n>` above for how the two options\n> +interact.\n\nHow about\n\nUse `-U<n>` to specify a minimum of context (default three lines).\n\nso that readers do not have to search.\n\n-- Hannes\n"},{"id":"417011","messageId":"87lfbo5sg8.fsf@protesilaos.com","threadId":"55144","inReplyTo":"87tuqebj6m.fsf@evledraar.gmail.com","subject":"Re: [PATCH] userdiff: add support for Emacs Lisp","fromName":"Protesilaos Stavrou","fromEmail":"info@protesilaos.com","sentAt":"2021-02-16T08:26:31Z","receivedAt":"2021-02-16T08:28:00Z","isPatch":true,"sender":{"key":"info@protesilaos.com","avatar":null},"body":"On 2021-02-14, 19:25 +0100, Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:\n\n> On Sun, Feb 14 2021, Johannes Sixt wrote:\n>\n>> Am 14.02.21 um 02:41 schrieb Ævar Arnfjörð Bjarmason:\n>>> Just a cursory \"git log -p -- lisp\" in emacs.git with your patch shows\n>>> e.g. lisp/thingatpt.el where forms in a \"defun\" aren't indented (before\n>>> it selects the \"defun\", after with yours it's a \"put\" in the middle of\n>>> the function).\n>>\n>> Note that negative matches can be specified. We use the feature in the\n>> cpp case to exclude public:/protected:/private: and label: that happen \n>> to be not indented. Perhaps that can be useful here?\n>>\n>> Oh, and BTW, what the patterns treat as \"function\" must not match what\n>> the language treats as function. The purpose of the hunk header is to \n>> spot a place in the source file easily. So, it does not hurt if\n>> eval-and-compile forms are captured (as was mentioned in the linked \n>> thread) if desired.\n>\n> Right, so having lots of test-case is helpful, e.g. for elisp maybe you\n> have a top-level defun, maybe not, maybe the top-level is a \"(progn\" so\n> you'd like a second-level more meaningful context, or maybe not...\n>\n> Obviously these userdiff patterns aren't a general parser and will\n> always be hit-and-miss, it's just useful to at least eyeball them\n> against in-the-wild test data to check their sanity & add some tests.\n\nOur intent with this patch is to rely on a heuristic that works for most\ncases.  There will always be some case that is not covered.  This point\nwas made explicit in the relevant emacs-devel thread.\n\n> My cursory glance at the emacs.git version v.s. what's being submitted\n> here is that this one does a worse job in *very common* cases.\n>\n>>> Yours also changes it from e.g.:\n>>>      @@ -61,7 +61,7 @@ forward-thing\n>>> to:\n>>>      @@ -61,7 +61,7 @@ (defun forward-thing (thing &optional n)\n>>> Is this really desired in elisp?\n\nI think this is a matter of perspective and indeed a valid reason for\ndoing this in emacs.git first.\n\nFor my part, I find the verbose style more informative, especially when\nit captures the previous form instead of the one directly around the\ndiffed lines.\n\nPerhaps one could add to that Emacs' ability to use the same name for\ndifferent things, e.g. a function and a variable, where verbosity of\nthis sort can help with disambiguation.  Granted, we should not really\non such a heading style to aid us in that task, though it may be\nsomething to consider.\n\n>> It's common practice to extract the entire line (sans indentation if\n>> applicable), not just the function name. Why would somebody want the \n>> latter? It doesn't carry as much information as could be possible.\n>\n> Because I'm familiar with the codebase I'm editing and I just need to\n> know that the diff I'm viewing is on the \"main\" function, not that it's\n> \"main()\", \"int main(int argv, char **argv)\", \"int main(const int argv,\n> const char *argv[])\", or to either have a \" {\" or not at the end\n> depending on the project's coding style.\n\nOftentimes we produce/read patches for projects that we are not\nnecessarily acquainted with.  This includes emacs.git and the multitude\nof Elisp packages in that milieu.  An extra element of contextuality can\ndo us good.\n\nIt is true that familiarity with the code base will always benefit from\nsuccinct headings, though I feel that whenever we are in doubt we should\nerr on the side of caution: which means that we must not introduce such\nan assumption to the workings of this piece of functionality.\n\n> I know our own userdiff builtin patterns don't do this, but it would be\n> very useful to retrofit this capability / maybe make it a configurable\n> feature, i.e. have them capture the meaningful part of the line, and you\n> could either print it all, or just that part.\n\nIt would be nice to have an option that toggles verbosity.  Though I\nguess this lies outside the scope of the patch in question.\n\nIn conclusion, I think we should decide on the next step forward: if you\nthink this should be applied to emacs.git before making its way to git\nitself, then we can move the discussion there.\n\nThanks for your time and efforts!\nProtesilaos or \"Prot\"\n\n-- \nProtesilaos Stavrou\nprotesilaos.com\n"},{"id":"417012","messageId":"87v9as9seu.fsf@evledraar.gmail.com","threadId":"55144","inReplyTo":"87lfbo5sg8.fsf@protesilaos.com","subject":"Re: [PATCH] userdiff: add support for Emacs Lisp","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-16T11:13:45Z","receivedAt":"2021-02-16T11:15:03Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Feb 16 2021, Protesilaos Stavrou wrote:\n\n> On 2021-02-14, 19:25 +0100, Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:\n>\n>> On Sun, Feb 14 2021, Johannes Sixt wrote:\n>>\n>>> Am 14.02.21 um 02:41 schrieb Ævar Arnfjörð Bjarmason:\n>>>> Just a cursory \"git log -p -- lisp\" in emacs.git with your patch shows\n>>>> e.g. lisp/thingatpt.el where forms in a \"defun\" aren't indented (before\n>>>> it selects the \"defun\", after with yours it's a \"put\" in the middle of\n>>>> the function).\n>>>\n>>> Note that negative matches can be specified. We use the feature in the\n>>> cpp case to exclude public:/protected:/private: and label: that happen \n>>> to be not indented. Perhaps that can be useful here?\n>>>\n>>> Oh, and BTW, what the patterns treat as \"function\" must not match what\n>>> the language treats as function. The purpose of the hunk header is to \n>>> spot a place in the source file easily. So, it does not hurt if\n>>> eval-and-compile forms are captured (as was mentioned in the linked \n>>> thread) if desired.\n>>\n>> Right, so having lots of test-case is helpful, e.g. for elisp maybe you\n>> have a top-level defun, maybe not, maybe the top-level is a \"(progn\" so\n>> you'd like a second-level more meaningful context, or maybe not...\n>>\n>> Obviously these userdiff patterns aren't a general parser and will\n>> always be hit-and-miss, it's just useful to at least eyeball them\n>> against in-the-wild test data to check their sanity & add some tests.\n>\n> Our intent with this patch is to rely on a heuristic that works for most\n> cases.  There will always be some case that is not covered.  This point\n> was made explicit in the relevant emacs-devel thread.\n\nYes these heuristics will always be bad in some cases. I'm just\nencouraging you to find some of the more common cases, make test-cases\nout of them, to say \"this is what I want\".\n\nThe most common difference I've seen is that by finding comments it\nmeans for changes like this:\n    \n    (def foo ()\n         \"...\"\n         (some-code)\n    ;; comments inside functions are often not indented\n       (blah))\n    \n    (def bar ()\n         \"...\"\n         CHANGE_ME)\n\nBefore we'd find \"foo\" as the context, now we find \";; comments inside\nfunctions are often not indented\".\n\nThere's other cases where that comment rule does the right\nthing. E.g. finding the description of the file at the top, but we could\nalso capture that with:\n\n    ^(;;; [^.]+\\.el ---.*)'\n\nThen you'd get things like:\n\n    ;;; button.el --- clickable buttons\n\nWithout overmatching any and all comments. Or maybe it isn't\novermatching and is better in the common case. I don't know.\n\nAnother example is now we'll match \"(progn\", sometimes that's \"right\",\nsometimes \"wrong\". Perhaps even in cases where that's \"right\" the common\n\"(progn\" pattern doesn't provide much/any extra information, and we'd be\nbetter to skip past it with a negative rule to the next \"(def\" ?\n\nI'm typing this in mu4e, but I wouldn't call myself deeply familiar with\nElisp. My reading of the linked thread / blog posts it links to is that\nsome other people came up with more narrow matching rules, presumably\nbecause they found some of these cases.\n\nSo all I'm suggesting is maybe pro-actively find those cases & loop\nthose people in.\n\n>> My cursory glance at the emacs.git version v.s. what's being submitted\n>> here is that this one does a worse job in *very common* cases.\n>>\n>>>> Yours also changes it from e.g.:\n>>>>      @@ -61,7 +61,7 @@ forward-thing\n>>>> to:\n>>>>      @@ -61,7 +61,7 @@ (defun forward-thing (thing &optional n)\n>>>> Is this really desired in elisp?\n>\n> I think this is a matter of perspective and indeed a valid reason for\n> doing this in emacs.git first.\n>\n> For my part, I find the verbose style more informative, especially when\n> it captures the previous form instead of the one directly around the\n> diffed lines.\n>\n> Perhaps one could add to that Emacs' ability to use the same name for\n> different things, e.g. a function and a variable, where verbosity of\n> this sort can help with disambiguation.  Granted, we should not really\n> on such a heading style to aid us in that task, though it may be\n> something to consider.\n\nI'm not saying it needs to be done in emacs.git first, just maybe\ncoordinate with people who wrote/use that rule.\n\nI think matching the whole line makes sense (sans leading whitespace),\nwe do it for the rest of the git.git rules.\n\n>>> It's common practice to extract the entire line (sans indentation if\n>>> applicable), not just the function name. Why would somebody want the \n>>> latter? It doesn't carry as much information as could be possible.\n>>\n>> Because I'm familiar with the codebase I'm editing and I just need to\n>> know that the diff I'm viewing is on the \"main\" function, not that it's\n>> \"main()\", \"int main(int argv, char **argv)\", \"int main(const int argv,\n>> const char *argv[])\", or to either have a \" {\" or not at the end\n>> depending on the project's coding style.\n>\n> Oftentimes we produce/read patches for projects that we are not\n> necessarily acquainted with.  This includes emacs.git and the multitude\n> of Elisp packages in that milieu.  An extra element of contextuality can\n> do us good.\n>\n> It is true that familiarity with the code base will always benefit from\n> succinct headings, though I feel that whenever we are in doubt we should\n> err on the side of caution: which means that we must not introduce such\n> an assumption to the workings of this piece of functionality.\n>\n>> I know our own userdiff builtin patterns don't do this, but it would be\n>> very useful to retrofit this capability / maybe make it a configurable\n>> feature, i.e. have them capture the meaningful part of the line, and you\n>> could either print it all, or just that part.\n>\n> It would be nice to have an option that toggles verbosity.  Though I\n> guess this lies outside the scope of the patch in question.\n\nYes I agree that this is completely outside the scope of adding elisp\nsupport to userdiff.\n\nI was just replying generally to Johaness on the topic of why someone\nwould want a pattern to match $1 over $0, as most of our git.git\npatterns do (or if it's $1, it's the whole line anyway, sans leading\nwhitespace).\n\n> In conclusion, I think we should decide on the next step forward: if you\n> think this should be applied to emacs.git before making its way to git\n> itself, then we can move the discussion there.\n\nI think just applying a version to this to git.git is fine, and it\nshould not wait for whatever 20-some patches test mechanism rewrite I\nstarted. I can rebase on top of this.\n\nB.t.w. emacs.git also has a rule for *.texi, perhaps you'd be interested\nin hacking that up too? :)\n"},{"id":"417077","messageId":"xmqq4kibsxyz.fsf@gitster.c.googlers.com","threadId":"55144","inReplyTo":"10e6cd64-8c1c-20ea-154e-89fad6664a5e@kdbg.org","subject":"Re: [PATCH 1/2] diff: do not display hunk context under -W","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-02-16T17:51:00Z","receivedAt":"2021-02-16T17:52:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Sixt <j6t@kdbg.org> writes:\n\n> Am 16.02.21 um 02:30 schrieb Junio C Hamano:\n>> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>>> This new behavior does give us the edge case that if we e.g. view the\n>>> diff here with \"-U150 -W\" we'd previously extend the context to the\n>>> middle of the \"is_func_rec()\" function, and show that function in the\n>>> hunk context. Now we'll show nothing.\n>> To me, that sounds like a grave regression.  Why lose the\n>> information?\n>> This may be coming from the difference between us, i.e. I read a lot\n>> more patches written by other people than my own changes written for\n>> my next commit, so every bit of hint helps, and the name of the\n>> function I am seeing its latter half in the precontext is sometimes\n>> a useful thing to see.\n>\n> I totally agree with your assessment. I wouldn't even have removed the\n> hunk header in the case of \"-W wins\", either, but that is a case that\n> I can live with when others think it makes sense.\n\nDitto.\n\nThe information on @@ ... @@ line may look misleading especially to\nthose who are not used to reading patches in the \"-W wins\" case.  It\nis otherwise not hurting anybody and it loses information to remove\nit, but it is not as grave as in the \"-U<n> wins\" case, so I do not\nmind losing it, if it helps them.\n\n\n"},{"id":"417083","messageId":"xmqqsg5vrhha.fsf@gitster.c.googlers.com","threadId":"55144","inReplyTo":"4bd7bb84-3b75-258e-b488-f66dff6ba6b5@kdbg.org","subject":"Re: [PATCH v2 09/27] userdiff tests: match full hunk headers","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-02-16T18:32:33Z","receivedAt":"2021-02-16T18:33:22Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Sixt <j6t@kdbg.org> writes:\n\n>>   t/t4018-diff-funcname.sh                      |  7 +++---\n>>   t/t4018/README                                | 22 +++++++++----------\n>>   t/t4018/README.ctx                            |  1 +\n>>   t/t4018/bash-arithmetic-function.ctx          |  1 +\n>>   t/t4018/bash-bashism-style-compact.ctx        |  1 +\n>>   [...and so on...]\n>\n> This is what I meant by \"without burdening test writers with lots of\n> subtleties\".\n>\n> I'm not a friend of this change :-(\n>\n> I think you are going overboard with required test precision. To have\n> useful tests for userdiff patterns that demonstrate its features, \n> authors should write *many* tests. The right balance should be on the\n> coverage of userdiff pattern features, not on the subtle details of\n> each and everyone of it. Requiring that many additional context files\n> makes it *really hard* to comply.\n\nYeah, the first time I saw the t4018 test framework appeared in my\ntree, I truly appreciated its simplicity, how the test input file is\nself-documenting and self-contained, with the clever use of \"RIGHT\",\n\"broken\" and \"ChangeMe\" magic tokens, admired the cleverness of the\napproach, and wished I was clever enough to invent that pattern to\napply to other tests myself.\n\nA little new for each and every test for the miniscule gain of\nchecking which part of the function header line is extracted feels a\nbit too much noise and rubs my sense of aesthetics, spoiled by the\nexisting t4018 tests, the wrong way.\n\nThis is a rough sketch of a different approach aiming for the same.\nI converted only a few files, but I hope that this is enough to\nillustrate the idea.\n\n t/t4018-diff-funcname.sh         | 17 ++++++++++++++---\n t/t4018/README                   |  9 ++++++---\n t/t4018/bash-arithmetic-function |  3 +++\n 3 files changed, 23 insertions(+), 6 deletions(-)\n\ndiff --git c/t/t4018-diff-funcname.sh w/t/t4018-diff-funcname.sh\nindex 9675bc17db..dd79c99fc5 100755\n--- c/t/t4018-diff-funcname.sh\n+++ w/t/t4018-diff-funcname.sh\n@@ -107,10 +107,21 @@ do\n \telse\n \t\tresult=success\n \tfi\n-\ttest_expect_$result \"hunk header: $i\" \"\n+\n+\ttest_expect_$result \"hunk header: $i\" '\n+\t\tHEAD=$(sed -n \\\n+\t\t\t-e \"s/^.*HEADER.*|\\(.*\\)right\\(.*\\)|.*/ \\1RIGHT\\2/p\" \"$i\") &&\n+\n \t\tgit diff -U1 $i >actual &&\n-\t\tgrep '@@ .* @@.*RIGHT' actual\n-\t\"\n+\n+\t\tsed -ne \"s/^@@[^@]*@@//p\" actual |\n+\t\tif test -n \"$HEAD\"\n+\t\tthen\n+\t\t\tgrep -F \"$HEAD\"\n+\t\telse\n+\t\t\tgrep \"^.*RIGHT\"\n+\t\tfi\n+\t'\n done\n \n test_done\ndiff --git c/t/t4018/README w/t/t4018/README\nindex 283e01cca1..bc3c11e083 100644\n--- c/t/t4018/README\n+++ w/t/t4018/README\n@@ -10,9 +10,12 @@ The text that must appear in the hunk header must contain the word\n To mark a test case that highlights a malfunction, insert the word\n BROKEN in all lower-case somewhere in the file.\n \n-This text is a bit twisted and out of order, but it is itself a\n-test case for the default hunk header pattern. Know what you are doing\n-if you change it.\n+This text is a bit twisted and out of order, but it is itself a test\n+case for the default hunk header pattern. Know what you are doing if\n+you change it.  You can optionally force exactly what should be on\n+the hunk header line by enclosing the expect text by vertical bars\n+(but downcasing the word right in it) and place it on a line with\n+HEADER on it, like |How to write right test cases|.\n \n BTW, this tests that the head line goes to the hunk header, not the line\n of equal signs.\ndiff --git c/t/t4018/bash-arithmetic-function w/t/t4018/bash-arithmetic-function\nindex c0b276cb50..935f18d96d 100644\n--- c/t/t4018/bash-arithmetic-function\n+++ w/t/t4018/bash-arithmetic-function\n@@ -2,3 +2,6 @@ RIGHT() ((\n \n     ChangeMe = \"$x\" + \"$y\"\n ))\n+\n+\n+# HEADER |right()|\n"},{"id":"417084","messageId":"xmqqo8gjrhcp.fsf@gitster.c.googlers.com","threadId":"55144","inReplyTo":"20210215154427.32693-13-avarab@gmail.com","subject":"Re: [PATCH v2 12/27] userdiff tests: rewrite hunk header test infrastructure","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-02-16T18:35:18Z","receivedAt":"2021-02-16T18:36:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Rewrite the hunk header test infrastructure introduced in\n> bfa7d01413 (t4018: an infrastructure to test hunk headers,\n> 2014-03-21).\n\nIs this \"oops, we added something unacceptably ugly in 09/27 and\nthis step fixes it\"?  Please don't.\n"},{"id":"417123","messageId":"xmqqeehfo9nn.fsf@gitster.c.googlers.com","threadId":"55144","inReplyTo":"20210215154427.32693-11-avarab@gmail.com","subject":"Re: [PATCH v2 10/27] blame tests: don't rely on t/t4018/ directory","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-02-16T23:49:48Z","receivedAt":"2021-02-16T23:50:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Refactor a test added in 9466e3809d (blame: enable funcname blaming\n> with userdiff driver, 2020-11-01) so that the blame tests don't rely\n> on stealing the contents of \"t/t4018/fortran-external-function\".\n\nI like changes like this and wish they (I haven't finished reading\nthe 27 patches, but I am hoping that there may be more) were\nseparately available ;-)\n"},{"id":"417124","messageId":"xmqqa6s3o9a8.fsf@gitster.c.googlers.com","threadId":"55144","inReplyTo":"20210215154427.32693-19-avarab@gmail.com","subject":"Re: [PATCH v2 18/27] gitattributes doc: reword discussion of built-in userdiff patterns","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-02-16T23:57:51Z","receivedAt":"2021-02-16T23:58:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Reword the discussion of the built-in userdiff patterns to make it\n> more natural to precede it with a discussion about the semantics of\n> pattern matching, instead of assuming that it follows right after the\n> \"diff.tex.xfuncname\" example which now immediately precedes it. This\n> will make a follow-up commit smaller.\n>\n> Helped-by: Eric Sunshine <sunshine@sunshineco.com>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  Documentation/gitattributes.txt | 16 +++++++++++-----\n>  1 file changed, 11 insertions(+), 5 deletions(-)\n\nGood changes.  The current text, which dates back to ae7aa499\n(Document custom hunk header selection, 2007-07-08), may have been\nadequate back when there were only \"java\" and \"tex\" built-in\npatterns but not anymore.\n\nI may (or may not) give the series a second pass to collect\nindependently salvageable bits, and if I did so, this would\nbe one of those that would be gleaned.\n\nThanks.\n\n> diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\n> index e84e104f93..62c1147ba9 100644\n> --- a/Documentation/gitattributes.txt\n> +++ b/Documentation/gitattributes.txt\n> @@ -794,11 +794,17 @@ backslashes; the pattern above picks a line that begins with a\n>  backslash, and zero or more occurrences of `sub` followed by\n>  `section` followed by open brace, to the end of line.\n>  \n> -There are a few built-in patterns to make this easier, and `tex`\n> -is one of them, so you do not have to write the above in your\n> -configuration file (you still need to enable this with the\n> -attribute mechanism, via `.gitattributes`).  The following built in\n> -patterns are available:\n> +There are built-in patterns shipped as part of git itself. A more\n> +advanced version of the `tex` pattern discussed above is one of them.\n> +\n> +For built-in patterns, you do not need `diff.<lang>.xfuncname` in your\n> +configuration file as discussed above, but if present, it will\n> +override a built-in pattern.\n> +\n> +Nevertheless, you need to enable built-in patterns via .gitattributes`\n> +for the pattern to take effect.\n> +\n> +The following built-in patterns are available:\n>  \n>  - `ada` suitable for source code in the Ada language.\n"},{"id":"417128","messageId":"xmqq4kibo907.fsf@gitster.c.googlers.com","threadId":"55144","inReplyTo":"20210215154427.32693-20-avarab@gmail.com","subject":"Re: [PATCH v2 19/27] gitattributes doc: document multi-line userdiff patterns","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-02-17T00:03:52Z","receivedAt":"2021-02-17T00:04:44Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> -There are built-in patterns shipped as part of git itself. A more\n> -advanced version of the `tex` pattern discussed above is one of them.\n> +Multiple patterns can be supplied by listing them one per line\n> +separated by `\\n`. They will be matched one at a time from left to\n> +right. Do not supply a trailing \"\\n\" for the last pattern. E.g.:\n\nI've always thought that this was modelled after \"grep --file=FILE\",\nwhere patterns are listed one per line, so \"from left to right\"\nlooked a bit funny, at least to me.\n\nIf the \"do not supply\" is because \"it would result in giving an\nempty string '' as the final pattern, which may not be what you\nwant\", then it is a good idea to spell it out.\n\n> +------------------------\n> +[diff \"perl\"]\n> +\txfuncname = \"!^=head\\n^[^ ]+.*\"\n> +------------------------\n> +\n> +Patterns in in a list of multiple that begin with \"!\" are negated. A\n\n\"in in\"???\n\n> +matching negated pattern will cause the matched line to be\n> +skipped. Use it to skip a later pattern that would otherwise match. It\n> +is an error if one or more negated patterns aren't followed by a\n> +non-negated pattern.\n> +\n> +To match a literal \"!\" at the start of a line, use some other regex\n> +construct that will match a literal \"!\" without \"!\" being the first\n> +character on that line, such as \"[!]\".\n> +\n> +If the pattern contains a `$1` capture it will be used instead of the\n> +entire matching line (`$0`) to display the hunk header. This can be\n> +used e.g. to strip whitespace from the beginning of the line, or to\n> +only display the function name as part of a longer function\n> +definition.\n> +\n> +There are built-in patterns shipped as part of git itself, see the\n> +full listing below.\n\nOther than the minor nits above, the new text is written very well.\n"},{"id":"417132","messageId":"87h7mba3h3.fsf@evledraar.gmail.com","threadId":"55144","inReplyTo":"1b2cb670-b49b-b478-7f69-6d4c356c8118@kdbg.org","subject":"Re: [PATCH v2 09/27] userdiff tests: match full hunk headers","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-17T01:27:04Z","receivedAt":"2021-02-17T01:27:49Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Feb 16 2021, Johannes Sixt wrote:\n\n> Am 16.02.21 um 19:32 schrieb Junio C Hamano:\n>> Johannes Sixt <j6t@kdbg.org> writes:\n>> \n>>>>    t/t4018-diff-funcname.sh                      |  7 +++---\n>>>>    t/t4018/README                                | 22 +++++++++----------\n>>>>    t/t4018/README.ctx                            |  1 +\n>>>>    t/t4018/bash-arithmetic-function.ctx          |  1 +\n>>>>    t/t4018/bash-bashism-style-compact.ctx        |  1 +\n>>>>    [...and so on...]\n>>>\n>>> This is what I meant by \"without burdening test writers with lots of\n>>> subtleties\".\n>>>\n>>> I'm not a friend of this change :-(\n>>>\n>>> I think you are going overboard with required test precision. To have\n>>> useful tests for userdiff patterns that demonstrate its features,\n>>> authors should write *many* tests. The right balance should be on the\n>>> coverage of userdiff pattern features, not on the subtle details of\n>>> each and everyone of it. Requiring that many additional context files\n>>> makes it *really hard* to comply.\n>> Yeah, the first time I saw the t4018 test framework appeared in my\n>> tree, I truly appreciated its simplicity, how the test input file is\n>> self-documenting and self-contained, with the clever use of \"RIGHT\",\n>> \"broken\" and \"ChangeMe\" magic tokens, admired the cleverness of the\n>> approach, and wished I was clever enough to invent that pattern to\n>> apply to other tests myself.\n>> A little new for each and every test for the miniscule gain of\n>> checking which part of the function header line is extracted feels a\n>> bit too much noise and rubs my sense of aesthetics, spoiled by the\n>> existing t4018 tests, the wrong way.\n>> This is a rough sketch of a different approach aiming for the same.\n>> I converted only a few files, but I hope that this is enough to\n>> illustrate the idea.\n>>   t/t4018-diff-funcname.sh         | 17 ++++++++++++++---\n>>   t/t4018/README                   |  9 ++++++---\n>>   t/t4018/bash-arithmetic-function |  3 +++\n>>   3 files changed, 23 insertions(+), 6 deletions(-)\n>> diff --git c/t/t4018-diff-funcname.sh w/t/t4018-diff-funcname.sh\n>> index 9675bc17db..dd79c99fc5 100755\n>> --- c/t/t4018-diff-funcname.sh\n>> +++ w/t/t4018-diff-funcname.sh\n>> @@ -107,10 +107,21 @@ do\n>>   \telse\n>>   \t\tresult=success\n>>   \tfi\n>> -\ttest_expect_$result \"hunk header: $i\" \"\n>> +\n>> +\ttest_expect_$result \"hunk header: $i\" '\n>> +\t\tHEAD=$(sed -n \\\n>> +\t\t\t-e \"s/^.*HEADER.*|\\(.*\\)right\\(.*\\)|.*/ \\1RIGHT\\2/p\" \"$i\") &&\n>> +\n>>   \t\tgit diff -U1 $i >actual &&\n>> -\t\tgrep '@@ .* @@.*RIGHT' actual\n>> -\t\"\n>> +\n>> +\t\tsed -ne \"s/^@@[^@]*@@//p\" actual |\n>> +\t\tif test -n \"$HEAD\"\n>> +\t\tthen\n>> +\t\t\tgrep -F \"$HEAD\"\n>> +\t\telse\n>> +\t\t\tgrep \"^.*RIGHT\"\n>> +\t\tfi\n>> +\t'\n>>   done\n>>     test_done\n>\n>> diff --git c/t/t4018/bash-arithmetic-function w/t/t4018/bash-arithmetic-function\n>> index c0b276cb50..935f18d96d 100644\n>> --- c/t/t4018/bash-arithmetic-function\n>> +++ w/t/t4018/bash-arithmetic-function\n>> @@ -2,3 +2,6 @@ RIGHT() ((\n>>         ChangeMe = \"$x\" + \"$y\"\n>>   ))\n>> +\n>> +\n>> +# HEADER |right()|\n>> \n>\n> Clever! Opt-in for those who desire precise tests.\n\nTests aren't only for testing a subjective \"good enough\" in the\nestimation of the author of the code in question, but also for others\nwho later touch the same area and want to avoid regressions.\n\nWhich is why I think it's an anti-pattern to use \"grep SOME-SUBSTR\" in\nlieu of test_cmp if we can easily do the latter.\n\nSo e.g. in this case part of my motivation is that this is one of the\nthings I want to look at porting to some general PCREv2 powered backend\nregex matching library once some of the pickaxe work I have pending\nlands.\n\nI'm very interested in whether such a port subtly breaks existing\nsemantics, and it's not useful if such regressions are hidden because\nsomeone who wrote a userdiff rule didn't think they needed to care about\nexact matching for their own purposes.\n\nI find this notion that patch authors who we'd expect to hack userdiff.c\nand somehow understand the arcane rules of the list form of\ndiff.<func>.xfunction (which wasn't even documented until this series),\nand to carefully read t/t4018/README to see how their test is parsed,\nwould be discouraged by some pretty plain shell syntax to pass in two\nstrings to be implausible.\n\nWhenever I've tried to hack up userdiff.c rules my first stumbling block\nhas been that if you fail the previous test gave you no output, because\nit used grep instead of test_cmp.\n\nSo you'd need to monkeypatch it to dump the value, or know about \"-d\"\nand trash directory inspection. So much for sparing prospective authors\nfrom the intricacies of our sh-based tests.\n\nThe old test format also forces you to try to mix real examples of a\nprogramming language with our need to shove an all-caps \"RIGHT\"\nidentifier somewhere on the hunk line.\n\nIn languages like Go, Perl or Elisp such an identifier is somewhere\nbetween so strongly discouraged that you'd never see one in the wild, or\nin others even a syntax error.\n\nIt seems much simpler to just ask the author to paste in their snippet,\ntest it, and then then paste what they got on the @@ line as another\nparameter to their test.\n"},{"id":"417133","messageId":"87eehfa36w.fsf@evledraar.gmail.com","threadId":"55144","inReplyTo":"c1e637d5-ad69-1726-2b38-476d0192ca02@kdbg.org","subject":"Re: [PATCH v2 11/27] blame tests: simplify userdiff driver test","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-17T01:33:11Z","receivedAt":"2021-02-17T01:34:12Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, Feb 15 2021, Johannes Sixt wrote:\n\n> Am 15.02.21 um 16:44 schrieb Ævar Arnfjörð Bjarmason:\n>> Simplify the test added in 9466e3809d (blame: enable funcname blaming\n>> with userdiff driver, 2020-11-01) to use the --author support recently\n>> added in 999cfc4f45 (test-lib functions: add --author support to\n>> test_commit, 2021-01-12).\n>> We also did not need the full fortran-external-function content,\n>> let's\n>> cut it down to just the important parts, and further modify it to\n>> demonstrate that the fortran-specific userdiff function is in effect\n>> by adding \"WRONG\" lines surrounding the \"RIGHT\" one.\n>> The test also left behind a .gitattributes files, let's clean it up\n>> with \"test_when_finished\".\n>> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>> ---\n>>   t/annotate-tests.sh | 36 +++++++++++++++---------------------\n>>   1 file changed, 15 insertions(+), 21 deletions(-)\n>> diff --git a/t/annotate-tests.sh b/t/annotate-tests.sh\n>> index 04a2c58594..4a86e0f349 100644\n>> --- a/t/annotate-tests.sh\n>> +++ b/t/annotate-tests.sh\n>> @@ -479,32 +479,26 @@ test_expect_success 'blame -L ^:RE (absolute: end-of-file)' '\n>>   \tcheck_count -f hello.c -L$n -L^:ma.. F 4 G 1 H 1\n>>   '\n>>   -test_expect_success 'setup -L :funcname with userdiff driver' '\n>> -\techo \"fortran-* diff=fortran\" >.gitattributes &&\n>> -\tfortran_file=fortran-external-function &&\n>> -\tcat >$fortran_file <<-\\EOF &&\n>> +test_expect_success 'blame -L :funcname with userdiff driver' '\n>> +\tcat >file.template <<-\\EOF &&\n>> +\tdef WRONG begin end\n>>   \tfunction RIGHT(a, b) result(c)\n>> +\tint WRONG(void) {}\n>>     \tinteger, intent(in) :: ChangeMe\n>> -\tinteger, intent(in) :: b\n>> -\tinteger, intent(out) :: c\n>> -\n>> -\tc = a+b\n>> -\n>> -\tend function RIGHT\n>>   \tEOF\n>> -\tgit add \"$fortran_file\" &&\n>> -\tGIT_AUTHOR_NAME=\"A\" GIT_AUTHOR_EMAIL=\"A@test.git\" \\\n>> -\tgit commit -m \"add fortran file\" &&\n>> -\tsed -e \"s/ChangeMe/IWasChanged/\" <\"$fortran_file\" >\"$fortran_file\".tmp &&\n>> -\tmv \"$fortran_file\".tmp \"$fortran_file\" &&\n>> -\tgit add \"$fortran_file\" &&\n>> -\tGIT_AUTHOR_NAME=\"B\" GIT_AUTHOR_EMAIL=\"B@test.git\" \\\n>> -\tgit commit -m \"change fortran file\"\n>> -'\n>>   -test_expect_success 'blame -L :funcname with userdiff driver' '\n>> -\tcheck_count -f fortran-external-function -L:RIGHT A 7 B 1\n>> +\tfortran_file=file.f03 &&\n>> +\ttest_when_finished \"rm .gitattributes\" &&\n>> +\techo \"$fortran_file diff=fortran\" >.gitattributes &&\n>> +\n>> +\ttest_commit --author \"A <A@test.git>\" \\\n>> +\t\t\"add\" $fortran_file \\\n>> +\t\t\"$(cat file.template)\" &&\n>> +\ttest_commit --author \"B <B@test.git>\" \\\n>> +\t\t\"change\" $fortran_file \\\n>> +\t\t\"$(cat file.template | sed -e s/ChangeMe/IWasChanged/)\" &&\n>> +\tcheck_count -f $fortran_file -L:RIGHT A 3 B 1\n>>   '\n>>     test_expect_success 'setup incremental' '\n>> \n>\n> I don't get the point. What do you need the tokens \"WRONG\" for when\n> they are not checked anywhere? Instead of adding unrelated lines (that\n> do not even look like Fortran), couldn't you just not remove some of\n> the others? In particular, the last one that contains \"RIGHT\" as well\n> may be useful to keep in order to show that the code is not confused\n> by it.\n\nIsn't the point of the test to assert that we're using a userdiff driver\nover the built-in xdiff rules here?\n\nWe can imagine that a change to its default heuristics would be to find\nthe first non-whitespace line, but it jumping over non-whitespace lines\nin search of a fortran-looking line doesn't seem like it would ever\nhappen. Hence the WRONG lines.\n\n> Please place \"$fortran_file\" in dquotes on the check_count line.\n\nWhy do we need to dquote a convenience variable defined in the test\nitself that'll never contain spaces or other funny things we'd get if we\nhad $(pwd) or whatever in there? It wouldn't hurt, but maybe I'm missing\nsome reason for why it's necessary or desired here.\n\n"},{"id":"417134","messageId":"xmqqr1lfmpz3.fsf@gitster.g","threadId":"55144","inReplyTo":"87eehfa36w.fsf@evledraar.gmail.com","subject":"Re: [PATCH v2 11/27] blame tests: simplify userdiff driver test","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-02-17T01:40:16Z","receivedAt":"2021-02-17T01:41:23Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n>>> +\tfortran_file=file.f03 &&\n>>> +\ttest_when_finished \"rm .gitattributes\" &&\n>>> +\techo \"$fortran_file diff=fortran\" >.gitattributes &&\n>>> +\n>>> +\ttest_commit --author \"A <A@test.git>\" \\\n>>> +\t\t\"add\" $fortran_file \\\n>>> +\t\t\"$(cat file.template)\" &&\n>>> +\ttest_commit --author \"B <B@test.git>\" \\\n>>> +\t\t\"change\" $fortran_file \\\n>>> +\t\t\"$(cat file.template | sed -e s/ChangeMe/IWasChanged/)\" &&\n>>> +\tcheck_count -f $fortran_file -L:RIGHT A 3 B 1\n> ...\n>> Please place \"$fortran_file\" in dquotes on the check_count line.\n>\n> Why do we need to dquote a convenience variable defined in the test\n> itself that'll never contain spaces or other funny things we'd get if we\n> had $(pwd) or whatever in there? It wouldn't hurt, but maybe I'm missing\n> some reason for why it's necessary or desired here.\n\nAlways dquoting when the code does not depend on splitting at IFS\nwhitespace reduces cognitive load.  The person who looks at the\nvariable reference, $fortran_file, has to wonder if the unquoted\nform is used to take advantage of being split at IFS whitespaces, or\nthe test author saved two keystrokes because the filename does not\nhave any such whitespace, and go back to the assignment to check.\n\n"},{"id":"417137","messageId":"xmqqim6rmox1.fsf@gitster.g","threadId":"55144","inReplyTo":"xmqqsg5vrhha.fsf@gitster.c.googlers.com","subject":"Re: [PATCH v2 09/27] userdiff tests: match full hunk headers","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-02-17T02:03:06Z","receivedAt":"2021-02-17T02:04:07Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> +\t\tsed -ne \"s/^@@[^@]*@@//p\" actual |\n> +\t\tif test -n \"$HEAD\"\n> +\t\tthen\n> +\t\t\tgrep -F \"$HEAD\"\n\nI think this should also use \"-x\" (match the whole line) if it is\nportable enough; the option should be available in POSIX.1 but we\nhaven't used it anywhere in our test suite.\n\n> +\t\telse\n> +\t\t\tgrep \"^.*RIGHT\"\n> +\t\tfi\n> +\t'\n>  done\n"},{"id":"417139","messageId":"875z2ra1bn.fsf@evledraar.gmail.com","threadId":"55144","inReplyTo":"xmqqim6rmox1.fsf@gitster.g","subject":"Re: [PATCH v2 09/27] userdiff tests: match full hunk headers","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-17T02:13:32Z","receivedAt":"2021-02-17T02:14:35Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Feb 17 2021, Junio C Hamano wrote:\n\n> Junio C Hamano <gitster@pobox.com> writes:\n>\n>> +\t\tsed -ne \"s/^@@[^@]*@@//p\" actual |\n>> +\t\tif test -n \"$HEAD\"\n>> +\t\tthen\n>> +\t\t\tgrep -F \"$HEAD\"\n>\n> I think this should also use \"-x\" (match the whole line) if it is\n> portable enough; the option should be available in POSIX.1 but we\n> haven't used it anywhere in our test suite.\n\nIsn't that just test_cmp with no debug output when it fails? Noted in\nhttps://lore.kernel.org/git/87h7mba3h3.fsf@evledraar.gmail.com/\n\n>> +\t\telse\n>> +\t\t\tgrep \"^.*RIGHT\"\n>> +\t\tfi\n>> +\t'\n>>  done\n\n"},{"id":"417141","messageId":"1b2cb670-b49b-b478-7f69-6d4c356c8118@kdbg.org","threadId":"55144","inReplyTo":"xmqqsg5vrhha.fsf@gitster.c.googlers.com","subject":"Re: [PATCH v2 09/27] userdiff tests: match full hunk headers","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2021-02-16T21:45:30Z","receivedAt":"2021-02-17T03:38:38Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 16.02.21 um 19:32 schrieb Junio C Hamano:\n> Johannes Sixt <j6t@kdbg.org> writes:\n> \n>>>    t/t4018-diff-funcname.sh                      |  7 +++---\n>>>    t/t4018/README                                | 22 +++++++++----------\n>>>    t/t4018/README.ctx                            |  1 +\n>>>    t/t4018/bash-arithmetic-function.ctx          |  1 +\n>>>    t/t4018/bash-bashism-style-compact.ctx        |  1 +\n>>>    [...and so on...]\n>>\n>> This is what I meant by \"without burdening test writers with lots of\n>> subtleties\".\n>>\n>> I'm not a friend of this change :-(\n>>\n>> I think you are going overboard with required test precision. To have\n>> useful tests for userdiff patterns that demonstrate its features,\n>> authors should write *many* tests. The right balance should be on the\n>> coverage of userdiff pattern features, not on the subtle details of\n>> each and everyone of it. Requiring that many additional context files\n>> makes it *really hard* to comply.\n> \n> Yeah, the first time I saw the t4018 test framework appeared in my\n> tree, I truly appreciated its simplicity, how the test input file is\n> self-documenting and self-contained, with the clever use of \"RIGHT\",\n> \"broken\" and \"ChangeMe\" magic tokens, admired the cleverness of the\n> approach, and wished I was clever enough to invent that pattern to\n> apply to other tests myself.\n> \n> A little new for each and every test for the miniscule gain of\n> checking which part of the function header line is extracted feels a\n> bit too much noise and rubs my sense of aesthetics, spoiled by the\n> existing t4018 tests, the wrong way.\n> \n> This is a rough sketch of a different approach aiming for the same.\n> I converted only a few files, but I hope that this is enough to\n> illustrate the idea.\n> \n>   t/t4018-diff-funcname.sh         | 17 ++++++++++++++---\n>   t/t4018/README                   |  9 ++++++---\n>   t/t4018/bash-arithmetic-function |  3 +++\n>   3 files changed, 23 insertions(+), 6 deletions(-)\n> \n> diff --git c/t/t4018-diff-funcname.sh w/t/t4018-diff-funcname.sh\n> index 9675bc17db..dd79c99fc5 100755\n> --- c/t/t4018-diff-funcname.sh\n> +++ w/t/t4018-diff-funcname.sh\n> @@ -107,10 +107,21 @@ do\n>   \telse\n>   \t\tresult=success\n>   \tfi\n> -\ttest_expect_$result \"hunk header: $i\" \"\n> +\n> +\ttest_expect_$result \"hunk header: $i\" '\n> +\t\tHEAD=$(sed -n \\\n> +\t\t\t-e \"s/^.*HEADER.*|\\(.*\\)right\\(.*\\)|.*/ \\1RIGHT\\2/p\" \"$i\") &&\n> +\n>   \t\tgit diff -U1 $i >actual &&\n> -\t\tgrep '@@ .* @@.*RIGHT' actual\n> -\t\"\n> +\n> +\t\tsed -ne \"s/^@@[^@]*@@//p\" actual |\n> +\t\tif test -n \"$HEAD\"\n> +\t\tthen\n> +\t\t\tgrep -F \"$HEAD\"\n> +\t\telse\n> +\t\t\tgrep \"^.*RIGHT\"\n> +\t\tfi\n> +\t'\n>   done\n>   \n>   test_done\n\n> diff --git c/t/t4018/bash-arithmetic-function w/t/t4018/bash-arithmetic-function\n> index c0b276cb50..935f18d96d 100644\n> --- c/t/t4018/bash-arithmetic-function\n> +++ w/t/t4018/bash-arithmetic-function\n> @@ -2,3 +2,6 @@ RIGHT() ((\n>   \n>       ChangeMe = \"$x\" + \"$y\"\n>   ))\n> +\n> +\n> +# HEADER |right()|\n> \n\nClever! Opt-in for those who desire precise tests.\n\n-- Hannes\n"},{"id":"417143","messageId":"200aecc0-4bfa-0655-da4d-45df7408d5ff@kdbg.org","threadId":"55144","inReplyTo":"87eehfa36w.fsf@evledraar.gmail.com","subject":"Re: [PATCH v2 11/27] blame tests: simplify userdiff driver test","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2021-02-17T07:10:26Z","receivedAt":"2021-02-17T07:11:06Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 17.02.21 um 02:33 schrieb Ævar Arnfjörð Bjarmason:\n> \n> On Mon, Feb 15 2021, Johannes Sixt wrote:\n> \n>> Am 15.02.21 um 16:44 schrieb Ævar Arnfjörð Bjarmason:\n>>> Simplify the test added in 9466e3809d (blame: enable funcname blaming\n>>> with userdiff driver, 2020-11-01) to use the --author support recently\n>>> added in 999cfc4f45 (test-lib functions: add --author support to\n>>> test_commit, 2021-01-12).\n>>> We also did not need the full fortran-external-function content,\n>>> let's\n>>> cut it down to just the important parts, and further modify it to\n>>> demonstrate that the fortran-specific userdiff function is in effect\n>>> by adding \"WRONG\" lines surrounding the \"RIGHT\" one.\n>>> The test also left behind a .gitattributes files, let's clean it up\n>>> with \"test_when_finished\".\n>>> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>>> ---\n>>>    t/annotate-tests.sh | 36 +++++++++++++++---------------------\n>>>    1 file changed, 15 insertions(+), 21 deletions(-)\n>>> diff --git a/t/annotate-tests.sh b/t/annotate-tests.sh\n>>> index 04a2c58594..4a86e0f349 100644\n>>> --- a/t/annotate-tests.sh\n>>> +++ b/t/annotate-tests.sh\n>>> @@ -479,32 +479,26 @@ test_expect_success 'blame -L ^:RE (absolute: end-of-file)' '\n>>>    \tcheck_count -f hello.c -L$n -L^:ma.. F 4 G 1 H 1\n>>>    '\n>>>    -test_expect_success 'setup -L :funcname with userdiff driver' '\n>>> -\techo \"fortran-* diff=fortran\" >.gitattributes &&\n>>> -\tfortran_file=fortran-external-function &&\n>>> -\tcat >$fortran_file <<-\\EOF &&\n>>> +test_expect_success 'blame -L :funcname with userdiff driver' '\n>>> +\tcat >file.template <<-\\EOF &&\n>>> +\tdef WRONG begin end\n>>>    \tfunction RIGHT(a, b) result(c)\n>>> +\tint WRONG(void) {}\n>>>      \tinteger, intent(in) :: ChangeMe\n>>> -\tinteger, intent(in) :: b\n>>> -\tinteger, intent(out) :: c\n>>> -\n>>> -\tc = a+b\n>>> -\n>>> -\tend function RIGHT\n>>>    \tEOF\n>>> -\tgit add \"$fortran_file\" &&\n>>> -\tGIT_AUTHOR_NAME=\"A\" GIT_AUTHOR_EMAIL=\"A@test.git\" \\\n>>> -\tgit commit -m \"add fortran file\" &&\n>>> -\tsed -e \"s/ChangeMe/IWasChanged/\" <\"$fortran_file\" >\"$fortran_file\".tmp &&\n>>> -\tmv \"$fortran_file\".tmp \"$fortran_file\" &&\n>>> -\tgit add \"$fortran_file\" &&\n>>> -\tGIT_AUTHOR_NAME=\"B\" GIT_AUTHOR_EMAIL=\"B@test.git\" \\\n>>> -\tgit commit -m \"change fortran file\"\n>>> -'\n>>>    -test_expect_success 'blame -L :funcname with userdiff driver' '\n>>> -\tcheck_count -f fortran-external-function -L:RIGHT A 7 B 1\n>>> +\tfortran_file=file.f03 &&\n>>> +\ttest_when_finished \"rm .gitattributes\" &&\n>>> +\techo \"$fortran_file diff=fortran\" >.gitattributes &&\n>>> +\n>>> +\ttest_commit --author \"A <A@test.git>\" \\\n>>> +\t\t\"add\" $fortran_file \\\n>>> +\t\t\"$(cat file.template)\" &&\n>>> +\ttest_commit --author \"B <B@test.git>\" \\\n>>> +\t\t\"change\" $fortran_file \\\n>>> +\t\t\"$(cat file.template | sed -e s/ChangeMe/IWasChanged/)\" &&\n>>> +\tcheck_count -f $fortran_file -L:RIGHT A 3 B 1\n>>>    '\n>>>      test_expect_success 'setup incremental' '\n>>>\n>>\n>> I don't get the point. What do you need the tokens \"WRONG\" for when\n>> they are not checked anywhere? Instead of adding unrelated lines (that\n>> do not even look like Fortran), couldn't you just not remove some of\n>> the others? In particular, the last one that contains \"RIGHT\" as well\n>> may be useful to keep in order to show that the code is not confused\n>> by it.\n> \n> Isn't the point of the test to assert that we're using a userdiff driver\n> over the built-in xdiff rules here?\n> \n> We can imagine that a change to its default heuristics would be to find\n> the first non-whitespace line, but it jumping over non-whitespace lines\n> in search of a fortran-looking line doesn't seem like it would ever\n> happen. Hence the WRONG lines.\n\nFair enough. A justification along these lines either as comment or in \nthe commit message would have helped; the existing text in the commit \nmessage was not sufficient to get the point across. I was distracted by \nthe capitalized \"WRONG\" token because we use a capitalized \"RIGHT\" token \nand check for it, so I expected that we also should check for \"WRONG\". \nWouldn't it then just be sufficient to remove the blank lines from the \nexiting Fortran content?\n\n>> Please place \"$fortran_file\" in dquotes on the check_count line.\n\nI just noticed that there are more unquoted expansions above that line. \n(I'm just mentioning it for completeness.)\n\n-- Hannes\n"},{"id":"417174","messageId":"xmqqk0r6ldrn.fsf@gitster.g","threadId":"55144","inReplyTo":"87h7mba3h3.fsf@evledraar.gmail.com","subject":"Re: [PATCH v2 09/27] userdiff tests: match full hunk headers","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-02-17T19:01:32Z","receivedAt":"2021-02-17T19:02:20Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n>>> +\t\tsed -ne \"s/^@@[^@]*@@//p\" actual |\n>>> +\t\tif test -n \"$HEAD\"\n>>> +\t\tthen\n>>> +\t\t\tgrep -F \"$HEAD\"\n>>> +\t\telse\n>>> +\t\t\tgrep \"^.*RIGHT\"\n>>> +\t\tfi\n>>> +\t'\n>>>   done\n>>>     test_done\n>>\n>>> diff --git c/t/t4018/bash-arithmetic-function w/t/t4018/bash-arithmetic-function\n>>> index c0b276cb50..935f18d96d 100644\n>>> --- c/t/t4018/bash-arithmetic-function\n>>> +++ w/t/t4018/bash-arithmetic-function\n>>> @@ -2,3 +2,6 @@ RIGHT() ((\n>>>         ChangeMe = \"$x\" + \"$y\"\n>>>   ))\n>>> +\n>>> +\n>>> +# HEADER |right()|\n>>> \n>>\n>> Clever! Opt-in for those who desire precise tests.\n>\n> Tests aren't only for testing a subjective \"good enough\" in the\n> estimation of the author of the code in question, but also for others\n> who later touch the same area and want to avoid regressions.\n>\n> Which is why I think it's an anti-pattern to use \"grep SOME-SUBSTR\" in\n> lieu of test_cmp if we can easily do the latter.\n\nSounds good.  It shouldn't be too hard to satisfy both camps,\ni.e. the quoted demonstrates one way to allow test writers to\ngive expectation in-place in the single test file, and replacing\nhow it uses \"grep\" to check the output with test_cmp or whatever\nwouldn't make the resulting tests too hard to write and maintain.\n\n"},{"id":"417562","messageId":"877dmz7wtv.fsf@evledraar.gmail.com","threadId":"55144","inReplyTo":"xmqqk0r6ldrn.fsf@gitster.g","subject":"Re: [PATCH v2 09/27] userdiff tests: match full hunk headers","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-23T13:11:40Z","receivedAt":"2021-02-23T13:12:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Feb 17 2021, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>\n>>>> +\t\tsed -ne \"s/^@@[^@]*@@//p\" actual |\n>>>> +\t\tif test -n \"$HEAD\"\n>>>> +\t\tthen\n>>>> +\t\t\tgrep -F \"$HEAD\"\n>>>> +\t\telse\n>>>> +\t\t\tgrep \"^.*RIGHT\"\n>>>> +\t\tfi\n>>>> +\t'\n>>>>   done\n>>>>     test_done\n>>>\n>>>> diff --git c/t/t4018/bash-arithmetic-function w/t/t4018/bash-arithmetic-function\n>>>> index c0b276cb50..935f18d96d 100644\n>>>> --- c/t/t4018/bash-arithmetic-function\n>>>> +++ w/t/t4018/bash-arithmetic-function\n>>>> @@ -2,3 +2,6 @@ RIGHT() ((\n>>>>         ChangeMe = \"$x\" + \"$y\"\n>>>>   ))\n>>>> +\n>>>> +\n>>>> +# HEADER |right()|\n>>>> \n>>>\n>>> Clever! Opt-in for those who desire precise tests.\n>>\n>> Tests aren't only for testing a subjective \"good enough\" in the\n>> estimation of the author of the code in question, but also for others\n>> who later touch the same area and want to avoid regressions.\n>>\n>> Which is why I think it's an anti-pattern to use \"grep SOME-SUBSTR\" in\n>> lieu of test_cmp if we can easily do the latter.\n>\n> Sounds good.  It shouldn't be too hard to satisfy both camps,\n> i.e. the quoted demonstrates one way to allow test writers to\n> give expectation in-place in the single test file, and replacing\n> how it uses \"grep\" to check the output with test_cmp or whatever\n> wouldn't make the resulting tests too hard to write and maintain.\n\nIt doesn't satisfy both camps, because I'd like to convert all these\ntests to test_cmp because for a subsequent refactoring of userdiff.c by\nme or others I don't know in advance what might break, so I'd like to\nassert the exact current behavior.\n\nWhereas your patch provides a way to opt-in individual tests to a\ntest_cmp-alike, but leaves the rest at grepping for the \"RIGHT\"\nsubstring. Failures in the tests who aren't opted-in will be hidden.\n\nIt also means that subsequent changes to the behavior in the form of\nsubmitted patches won't be as self-documenting, e.g. I've wondered if we\ncould introduce a case to balance parens in this code (sometimes C\nfunction declarations stretch across lines), and there's e.g. the\narbitrary limit of 80 bytes on the line (which to be fair, we don't\ncurretly have tests for).\n\nAnyway, as noted in [1] I don't see how this custom format of grepping\nstuff out of plain-text files is simpler, particularly when its behavior\nwould start to rely on other things like \"# HEADER |right()|\" whose\nbehavior is a function of what we grep/sed when/where in the logic\ndriving the tests.\n\nBut if you & Johannes S. disagree with that I don't really say a way\nforward with this series. I think e.g. squashing 09/27 into the rest\nwould make things simpler/less verbose, but the end-state would still be\nmatching the full hunk line, and if that's not something that's wanted\nin any shape or form as a default...\n\n1. https://lore.kernel.org/git/87h7mba3h3.fsf@evledraar.gmail.com/\n"},{"id":"417620","messageId":"1c5f8cb8-2d99-4279-ba61-4f7720ece277@kdbg.org","threadId":"55144","inReplyTo":"877dmz7wtv.fsf@evledraar.gmail.com","subject":"Re: [PATCH v2 09/27] userdiff tests: match full hunk headers","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2021-02-23T21:02:30Z","receivedAt":"2021-02-23T21:04:58Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 23.02.21 um 14:11 schrieb Ævar Arnfjörð Bjarmason:\n> On Wed, Feb 17 2021, Junio C Hamano wrote:\n>> Sounds good.  It shouldn't be too hard to satisfy both camps,\n>> i.e. the quoted demonstrates one way to allow test writers to\n>> give expectation in-place in the single test file, and replacing\n>> how it uses \"grep\" to check the output with test_cmp or whatever\n>> wouldn't make the resulting tests too hard to write and maintain.\n> \n> It doesn't satisfy both camps, because I'd like to convert all these\n> tests to test_cmp because for a subsequent refactoring of userdiff.c by\n> me or others I don't know in advance what might break, so I'd like to\n> assert the exact current behavior.\n> \n> Whereas your patch provides a way to opt-in individual tests to a\n> test_cmp-alike, but leaves the rest at grepping for the \"RIGHT\"\n> substring. Failures in the tests who aren't opted-in will be hidden.\n> \n> It also means that subsequent changes to the behavior in the form of\n> submitted patches won't be as self-documenting, e.g. I've wondered if we\n> could introduce a case to balance parens in this code (sometimes C\n> function declarations stretch across lines), and there's e.g. the\n> arbitrary limit of 80 bytes on the line (which to be fair, we don't\n> curretly have tests for).\n> \n> Anyway, as noted in [1] I don't see how this custom format of grepping\n> stuff out of plain-text files is simpler, particularly when its behavior\n> would start to rely on other things like \"# HEADER |right()|\" whose\n> behavior is a function of what we grep/sed when/where in the logic\n> driving the tests.\n> \n> But if you & Johannes S. disagree with that I don't really say a way\n> forward with this series. I think e.g. squashing 09/27 into the rest\n> would make things simpler/less verbose, but the end-state would still be\n> matching the full hunk line, and if that's not something that's wanted\n> in any shape or form as a default...\n> \n> 1. https://lore.kernel.org/git/87h7mba3h3.fsf@evledraar.gmail.com/\n\nI could live with a version of Junio's suggestion that is not opt-in,\ni.e., the checks are mandatory and exact. The important point is that\nthere is only one file per test case; that would still count as\n\"sufficiently simple\" in my book.\n\n-- Hannes\n"},{"id":"417675","messageId":"87y2fd7m90.fsf@evledraar.gmail.com","threadId":"55144","inReplyTo":"1c5f8cb8-2d99-4279-ba61-4f7720ece277@kdbg.org","subject":"Re: [PATCH v2 09/27] userdiff tests: match full hunk headers","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-24T11:12:27Z","receivedAt":"2021-02-24T11:15:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Feb 23 2021, Johannes Sixt wrote:\n\n> Am 23.02.21 um 14:11 schrieb Ævar Arnfjörð Bjarmason:\n>> On Wed, Feb 17 2021, Junio C Hamano wrote:\n>>> Sounds good.  It shouldn't be too hard to satisfy both camps,\n>>> i.e. the quoted demonstrates one way to allow test writers to\n>>> give expectation in-place in the single test file, and replacing\n>>> how it uses \"grep\" to check the output with test_cmp or whatever\n>>> wouldn't make the resulting tests too hard to write and maintain.\n>> \n>> It doesn't satisfy both camps, because I'd like to convert all these\n>> tests to test_cmp because for a subsequent refactoring of userdiff.c by\n>> me or others I don't know in advance what might break, so I'd like to\n>> assert the exact current behavior.\n>> \n>> Whereas your patch provides a way to opt-in individual tests to a\n>> test_cmp-alike, but leaves the rest at grepping for the \"RIGHT\"\n>> substring. Failures in the tests who aren't opted-in will be hidden.\n>> \n>> It also means that subsequent changes to the behavior in the form of\n>> submitted patches won't be as self-documenting, e.g. I've wondered if we\n>> could introduce a case to balance parens in this code (sometimes C\n>> function declarations stretch across lines), and there's e.g. the\n>> arbitrary limit of 80 bytes on the line (which to be fair, we don't\n>> curretly have tests for).\n>> \n>> Anyway, as noted in [1] I don't see how this custom format of grepping\n>> stuff out of plain-text files is simpler, particularly when its behavior\n>> would start to rely on other things like \"# HEADER |right()|\" whose\n>> behavior is a function of what we grep/sed when/where in the logic\n>> driving the tests.\n>> \n>> But if you & Johannes S. disagree with that I don't really say a way\n>> forward with this series. I think e.g. squashing 09/27 into the rest\n>> would make things simpler/less verbose, but the end-state would still be\n>> matching the full hunk line, and if that's not something that's wanted\n>> in any shape or form as a default...\n>> \n>> 1. https://lore.kernel.org/git/87h7mba3h3.fsf@evledraar.gmail.com/\n>\n> I could live with a version of Junio's suggestion that is not opt-in,\n> i.e., the checks are mandatory and exact. The important point is that\n> there is only one file per test case; that would still count as\n> \"sufficiently simple\" in my book.\n\nSo something where the tests in 14-15,17,19/27 would need to go back to\nhaving the config that's part of the test driven by setup in\nt/t4018-diff-funcname.sh, and the contents of a test file being e.g. (to\ntake the test from 25/27):\n\n    # HEADER |package main|\n    package main\n\n    import \"fmt\"\n    // ChangeMe\n\n?\n"},{"id":"417684","messageId":"b8c26fac-85e7-8565-7469-6b3fe965a570@kdbg.org","threadId":"55144","inReplyTo":"87y2fd7m90.fsf@evledraar.gmail.com","subject":"Re: [PATCH v2 09/27] userdiff tests: match full hunk headers","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2021-02-24T17:13:31Z","receivedAt":"2021-02-24T17:14:12Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 24.02.21 um 12:12 schrieb Ævar Arnfjörð Bjarmason:\n> \n> On Tue, Feb 23 2021, Johannes Sixt wrote:\n>> I could live with a version of Junio's suggestion that is not opt-in,\n>> i.e., the checks are mandatory and exact. The important point is that\n>> there is only one file per test case; that would still count as\n>> \"sufficiently simple\" in my book.\n> \n> So something where the tests in 14-15,17,19/27 would need to go back to\n> having the config that's part of the test driven by setup in\n> t/t4018-diff-funcname.sh,\n\nI'm afraid, I don't understand what you mean by this sentence. Do you\nmean \"not do the refactoring like in those patches\"? Yes, that would\nlikely be unnecessary churn.\n\n> and the contents of a test file being e.g. (to\n> take the test from 25/27):\n> \n>     # HEADER |package main|\n>     package main\n> \n>     import \"fmt\"\n>     // ChangeMe\n> \n> ?\n> \n\nYes. Except that I would move the \"expected text\" part to the end of the\nfile so that there is no danger that it is mistaken as a hunk header (in\ncase that a pattern is so loose that it matches that line).\n\n-- Hannes\n"},{"id":"417717","messageId":"20210224195129.4004-1-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"[PATCH v3 00/35] 20210215154427.32693-1-avarab@gmail.com","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-24T19:50:54Z","receivedAt":"2021-02-24T19:52:56Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Addresses feedback on v2. Since Junio & Johannes expressed a desire to\nkeep the existing test scheme in t4018/* it's still there, but it's\nalso possible to add *.sh tests in that directory to use the more\nfamiliar test framework used elsewhere in the test suite.\n\nThe tests added here make use of it to e.g. supply custom -U<n>\narguments, set config before the tests etc.\n\nI also improved that existing test support so you can have N tests in\none file with (mostly) the existing test syntax. See the \"userdiff\ntests: add a test with multiple tests in a LANG file\" patch.\n\nRange-diff below.\n\nÆvar Arnfjörð Bjarmason (35):\n  userdiff: refactor away the parse_bool() function\n  userdiff style: re-order drivers in alphabetical order\n  userdiff style: declare patterns with consistent style\n  userdiff style: normalize pascal regex declaration\n  userdiff: add and use for_each_userdiff_driver()\n  userdiff tests: explicitly test \"default\" pattern\n  userdiff tests: list builtin drivers via test-tool\n  userdiff: remove support for \"broken\" tests\n  blame tests: don't rely on t/t4018/ directory\n  blame tests: simplify userdiff driver test\n  userdiff tests: match full hunk headers\n  userdiff tests: change setup loop to individual test setup\n  userdiff tests: factor out test_diff_funcname() logic\n  userdiff tests: add alternative hunk header test infrastructure\n  userdiff tests: add a test with multiple tests in a LANG file\n  userdiff tests: do config teardown in test_diff_funcname()\n  userdiff tests: move custom patterns into one test file\n  userdiff tests: remove hack for \"RIGHT\" token\n  userdiff tests: do not do compile tests on \"custom\" pattern\n  userdiff tests: assert that new built-in drivers have tests\n  userdiff tests + docs: document & test \"diff.<driver>.x?funcname\"\n  gitattributes doc: reword discussion of built-in userdiff patterns\n  userdiff tests: move perl tests to perl.sh\n  userdiff tests: move away from \"RIGHT\" in perl.sh\n  gitattributes doc: document multi-line userdiff patterns\n  userdiff tests: switch to -U0 by default\n  userdiff tests: remove \"funcname\" from custom3 test\n  userdiff tests: assert empty hunk header context on -U<large>\n  userdiff tests: test for a bug in 1dbf0c0ad6c\n  userdiff golang: simplify and correct matching regex\n  userdiff golang: don't over-match intented constructs\n  userdiff golang: add a rule to match \"package\"\n  userdiff golang: match multi-line \"const\" and \"import\"\n  userdiff tests: add basic test for ada\n  userdiff tests: add basic test for ruby\n\n Documentation/config/diff.txt              |  11 ++\n Documentation/gitattributes.txt            |  46 ++++-\n Makefile                                   |   1 +\n t/annotate-tests.sh                        |  34 ++--\n t/helper/test-tool.c                       |   1 +\n t/helper/test-tool.h                       |   1 +\n t/helper/test-userdiff.c                   |  31 ++++\n t/t4018-diff-funcname.sh                   | 180 +++++++++++++-------\n t/t4018/README                             |  69 ++++++--\n t/t4018/ada.sh                             |  37 ++++\n t/t4018/bash-arithmetic-function           |   1 +\n t/t4018/bash-bashism-style-compact         |   1 +\n t/t4018/bash-bashism-style-function        |   1 +\n t/t4018/bash-bashism-style-whitespace      |   1 +\n t/t4018/bash-conditional-function          |   1 +\n t/t4018/bash-missing-parentheses           |   1 +\n t/t4018/bash-mixed-style-compact           |   1 +\n t/t4018/bash-mixed-style-function          |   1 +\n t/t4018/bash-nested-functions              |   1 +\n t/t4018/bash-other-characters              |   1 +\n t/t4018/bash-posix-style-compact           |   1 +\n t/t4018/bash-posix-style-function          |   1 +\n t/t4018/bash-posix-style-whitespace        |   1 +\n t/t4018/bash-subshell-function             |   1 +\n t/t4018/bash-trailing-comment              |   1 +\n t/t4018/cpp-c++-function                   |   1 +\n t/t4018/cpp-class-constructor              |   1 +\n t/t4018/cpp-class-constructor-mem-init     |   1 +\n t/t4018/cpp-class-definition               |   1 +\n t/t4018/cpp-class-definition-derived       |   1 +\n t/t4018/cpp-class-destructor               |   1 +\n t/t4018/cpp-function-returning-global-type |   1 +\n t/t4018/cpp-function-returning-nested      |   1 +\n t/t4018/cpp-function-returning-pointer     |   1 +\n t/t4018/cpp-function-returning-reference   |   1 +\n t/t4018/cpp-gnu-style-function             |   1 +\n t/t4018/cpp-namespace-definition           |   1 +\n t/t4018/cpp-operator-definition            |   1 +\n t/t4018/cpp-skip-access-specifiers         |   1 +\n t/t4018/cpp-skip-comment-block             |   1 +\n t/t4018/cpp-skip-labels                    |   1 +\n t/t4018/cpp-struct-definition              |   1 +\n t/t4018/cpp-struct-single-line             |   1 +\n t/t4018/cpp-template-function-definition   |   1 +\n t/t4018/cpp-union-definition               |   1 +\n t/t4018/cpp-void-c-function                |   1 +\n t/t4018/css-attribute-value-selector       |   1 +\n t/t4018/css-block-level-@-statements       |   1 +\n t/t4018/css-brace-in-col-1                 |   1 +\n t/t4018/css-class-selector                 |   1 +\n t/t4018/css-colon-eol                      |   1 +\n t/t4018/css-colon-selector                 |   1 +\n t/t4018/css-common                         |   1 +\n t/t4018/css-id-selector                    |   1 +\n t/t4018/css-long-selector-list             |   1 +\n t/t4018/css-prop-sans-indent               |   1 +\n t/t4018/css-root-selector                  |   1 +\n t/t4018/css-short-selector-list            |   1 +\n t/t4018/css-trailing-space                 |   1 +\n t/t4018/custom.sh                          | 183 ++++++++++++++++++++\n t/t4018/custom1-pattern                    |  17 --\n t/t4018/custom2-match-to-end-of-line       |   8 -\n t/t4018/custom3-alternation-in-pattern     |  17 --\n t/t4018/dts-labels                         |   1 +\n t/t4018/dts-node-unitless                  |   1 +\n t/t4018/dts-nodes                          |   1 +\n t/t4018/dts-nodes-boolean-prop             |   1 +\n t/t4018/dts-nodes-comment1                 |   1 +\n t/t4018/dts-nodes-comment2                 |   1 +\n t/t4018/dts-nodes-multiline-prop           |   1 +\n t/t4018/dts-reference                      |   1 +\n t/t4018/dts-root                           |   1 +\n t/t4018/dts-root-comment                   |   1 +\n t/t4018/elixir-do-not-pick-end             |   1 +\n t/t4018/elixir-ex-unit-test                |   1 +\n t/t4018/elixir-function                    |   1 +\n t/t4018/elixir-macro                       |   1 +\n t/t4018/elixir-module                      |   1 +\n t/t4018/elixir-module-func                 |   1 +\n t/t4018/elixir-nested-module               |   1 +\n t/t4018/elixir-private-function            |   1 +\n t/t4018/elixir-protocol                    |   1 +\n t/t4018/elixir-protocol-implementation     |   1 +\n t/t4018/fortran-block-data                 |   1 +\n t/t4018/fortran-comment                    |   1 +\n t/t4018/fortran-comment-keyword            |   1 +\n t/t4018/fortran-comment-legacy             |   1 +\n t/t4018/fortran-comment-legacy-star        |   1 +\n t/t4018/fortran-external-function          |   1 +\n t/t4018/fortran-external-subroutine        |   1 +\n t/t4018/fortran-module                     |   1 +\n t/t4018/fortran-module-procedure           |   1 +\n t/t4018/fortran-program                    |   1 +\n t/t4018/fountain-scene                     |   1 +\n t/t4018/golang                             | 148 ++++++++++++++++\n t/t4018/golang-complex-function            |   8 -\n t/t4018/golang-func                        |   4 -\n t/t4018/golang-interface                   |   4 -\n t/t4018/golang-long-func                   |   5 -\n t/t4018/golang-struct                      |   4 -\n t/t4018/java-class-member-function         |   1 +\n t/t4018/markdown-heading-indented          |   1 +\n t/t4018/markdown-heading-non-headings      |   1 +\n t/t4018/matlab-class-definition            |   1 +\n t/t4018/matlab-function                    |   1 +\n t/t4018/matlab-octave-section-1            |   1 +\n t/t4018/matlab-octave-section-2            |   1 +\n t/t4018/matlab-section                     |   1 +\n t/t4018/perl-skip-end-of-heredoc           |   8 -\n t/t4018/perl-skip-forward-decl             |  10 --\n t/t4018/perl-skip-sub-in-pod               |  18 --\n t/t4018/perl-sub-definition                |   4 -\n t/t4018/perl-sub-definition-kr-brace       |   4 -\n t/t4018/perl.sh                            |  93 +++++++++++\n t/t4018/php-abstract-class                 |   1 +\n t/t4018/php-abstract-method                |   1 +\n t/t4018/php-class                          |   1 +\n t/t4018/php-final-class                    |   1 +\n t/t4018/php-final-method                   |   1 +\n t/t4018/php-function                       |   1 +\n t/t4018/php-interface                      |   1 +\n t/t4018/php-method                         |   1 +\n t/t4018/php-trait                          |   1 +\n t/t4018/python-async-def                   |   1 +\n t/t4018/python-class                       |   1 +\n t/t4018/python-def                         |   1 +\n t/t4018/python-indented-async-def          |   1 +\n t/t4018/python-indented-class              |   1 +\n t/t4018/python-indented-def                |   1 +\n t/t4018/ruby.sh                            |  58 +++++++\n t/t4018/rust-fn                            |   1 +\n t/t4018/rust-impl                          |   1 +\n t/t4018/rust-macro-rules                   |   1 +\n t/t4018/rust-struct                        |   1 +\n t/t4018/rust-trait                         |   1 +\n userdiff.c                                 | 186 +++++++++++++--------\n userdiff.h                                 |  15 ++\n 137 files changed, 1036 insertions(+), 277 deletions(-)\n create mode 100644 t/helper/test-userdiff.c\n create mode 100755 t/t4018/ada.sh\n create mode 100755 t/t4018/custom.sh\n delete mode 100644 t/t4018/custom1-pattern\n delete mode 100644 t/t4018/custom2-match-to-end-of-line\n delete mode 100644 t/t4018/custom3-alternation-in-pattern\n create mode 100644 t/t4018/golang\n delete mode 100644 t/t4018/golang-complex-function\n delete mode 100644 t/t4018/golang-func\n delete mode 100644 t/t4018/golang-interface\n delete mode 100644 t/t4018/golang-long-func\n delete mode 100644 t/t4018/golang-struct\n delete mode 100644 t/t4018/perl-skip-end-of-heredoc\n delete mode 100644 t/t4018/perl-skip-forward-decl\n delete mode 100644 t/t4018/perl-skip-sub-in-pod\n delete mode 100644 t/t4018/perl-sub-definition\n delete mode 100644 t/t4018/perl-sub-definition-kr-brace\n create mode 100755 t/t4018/perl.sh\n create mode 100755 t/t4018/ruby.sh\n\nRange-diff:\n 1:  305fc646d0d =  1:  0be132b05e2 userdiff: refactor away the parse_bool() function\n 2:  989438c46ae =  2:  d1e00a739ac userdiff style: re-order drivers in alphabetical order\n 3:  4c48e5532ce =  3:  b99bd158d45 userdiff style: declare patterns with consistent style\n 4:  f41fa5b316f =  4:  9ce6d47021c userdiff style: normalize pascal regex declaration\n 5:  0875d5205c1 =  5:  369fbdcee83 userdiff: add and use for_each_userdiff_driver()\n 6:  638247d04d5 =  6:  70d62a97211 userdiff tests: explicitly test \"default\" pattern\n 7:  219043a4881 !  7:  792421a2f8b userdiff tests: list builtin drivers via test-tool\n    @@ t/helper/test-userdiff.c (new)\n     +static int driver_cb(struct userdiff_driver *driver,\n     +\t\t     enum userdiff_driver_type type, void *priv)\n     +{\n    -+\tputs(driver->name);\n    ++\tif (driver->funcname.pattern)\n    ++\t\tputs(driver->name);\n     +\treturn 0;\n     +}\n     +\n    @@ t/t4018-diff-funcname.sh: test_description='Test custom diff function name patte\n      . ./test-lib.sh\n      \n      test_expect_success 'setup' '\n    -+\tbuiltin_drivers=$(test-tool userdiff list-builtin-drivers) &&\n    -+\ttest -n \"$builtin_drivers\" &&\n    ++\t# Make sure additions to builtin_drivers are sorted\n    ++\ttest_when_finished \"rm builtin-drivers.sorted\" &&\n    ++\ttest-tool userdiff list-builtin-drivers >builtin-drivers &&\n    ++\ttest_file_not_empty builtin-drivers &&\n    ++\tsort <builtin-drivers >builtin-drivers.sorted &&\n    ++\ttest_cmp builtin-drivers.sorted builtin-drivers &&\n     +\n      \t# a non-trivial custom pattern\n      \tgit config diff.custom1.funcname \"!static\n    @@ t/t4018-diff-funcname.sh: test_expect_success 'setup' '\n     -\trust\n     -\ttex\n     -\tdefault\n    -+\t$builtin_drivers\n    ++\t$(cat builtin-drivers)\n      \tcustom1\n      \tcustom2\n      \tcustom3\n 8:  eb66160aac7 =  8:  9081e2a152e userdiff: remove support for \"broken\" tests\n 9:  c6c54039e27 <  -:  ----------- userdiff tests: match full hunk headers\n10:  1c6ddf96f61 !  9:  d3652f95d5e blame tests: don't rely on t/t4018/ directory\n    @@ Commit message\n         with userdiff driver, 2020-11-01) so that the blame tests don't rely\n         on stealing the contents of \"t/t4018/fortran-external-function\".\n     \n    -    I'm about to refactor that directory to delete that file, just moving\n    -    the relevant test file here inline is the easiest solution, and I\n    -    think also the most readable.\n    +    I'm about to change that file in a subsequent commit. Just moving the\n    +    relevant test file here inline is the easiest solution, and I think\n    +    also the most readable.\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n11:  8a883d87990 ! 10:  35d12779ea1 blame tests: simplify userdiff driver test\n    @@ Commit message\n         added in 999cfc4f45 (test-lib functions: add --author support to\n         test_commit, 2021-01-12).\n     \n    -    We also did not need the full fortran-external-function content, let's\n    +    We also did not need the full fortran-external-function content. Let's\n         cut it down to just the important parts, and further modify it to\n         demonstrate that the fortran-specific userdiff function is in effect\n    -    by adding \"WRONG\" lines surrounding the \"RIGHT\" one.\n    +    by adding \"DO NOT MATCH ...\" and \"AS THE ...\" lines surrounding the\n    +    \"RIGHT\" one. This is to check that we're using the userdiff \"fortran\"\n    +    driver, as opposed to the default driver.\n     \n         The test also left behind a .gitattributes files, let's clean it up\n         with \"test_when_finished\".\n    @@ t/annotate-tests.sh: test_expect_success 'blame -L ^:RE (absolute: end-of-file)'\n     -\tcat >$fortran_file <<-\\EOF &&\n     +test_expect_success 'blame -L :funcname with userdiff driver' '\n     +\tcat >file.template <<-\\EOF &&\n    -+\tdef WRONG begin end\n    ++\tDO NOT MATCH THIS LINE\n      \tfunction RIGHT(a, b) result(c)\n    -+\tint WRONG(void) {}\n    ++\tAS THE DEFAULT DRIVER WOULD\n      \n      \tinteger, intent(in) :: ChangeMe\n     -\tinteger, intent(in) :: b\n    @@ t/annotate-tests.sh: test_expect_success 'blame -L ^:RE (absolute: end-of-file)'\n     +\techo \"$fortran_file diff=fortran\" >.gitattributes &&\n     +\n     +\ttest_commit --author \"A <A@test.git>\" \\\n    -+\t\t\"add\" $fortran_file \\\n    ++\t\t\"add\" \"$fortran_file\" \\\n     +\t\t\"$(cat file.template)\" &&\n     +\ttest_commit --author \"B <B@test.git>\" \\\n    -+\t\t\"change\" $fortran_file \\\n    ++\t\t\"change\" \"$fortran_file\" \\\n     +\t\t\"$(cat file.template | sed -e s/ChangeMe/IWasChanged/)\" &&\n    -+\tcheck_count -f $fortran_file -L:RIGHT A 3 B 1\n    ++\tcheck_count -f \"$fortran_file\" -L:RIGHT A 3 B 1\n      '\n      \n      test_expect_success 'setup incremental' '\n12:  e56a7a6b5f4 <  -:  ----------- userdiff tests: rewrite hunk header test infrastructure\n -:  ----------- > 11:  4bd8a0daa25 userdiff tests: match full hunk headers\n -:  ----------- > 12:  d2d74476f2a userdiff tests: change setup loop to individual test setup\n21:  9a18506aff8 ! 13:  8db95a69924 userdiff tests: factor out test_diff_funcname() logic\n    @@ Commit message\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## t/t4018-diff-funcname.sh ##\n    -@@ t/t4018-diff-funcname.sh: test_expect_success 'last regexp must not be negated' '\n    - \ttest_i18ngrep \": Last expression must not be negated:\" msg\n    +@@ t/t4018-diff-funcname.sh: test_expect_success 'setup hunk header tests' '\n    + \tgit -C t4018 add .\n      '\n      \n     +do_change_me () {\n    @@ t/t4018-diff-funcname.sh: test_expect_success 'last regexp must not be negated'\n     +\tsed -n -e \"s/^.*@@$//p\" -e \"s/^.*@@ //p\" <$file\n     +}\n     +\n    - test_diff_funcname () {\n    - \tdesc=$1\n    - \tcat <&8 >arg.header &&\n    -@@ t/t4018-diff-funcname.sh: test_diff_funcname () {\n    - \t\tcp arg.test \"$what\" &&\n    - \t\tcp arg.header expected &&\n    - \t\tgit add \"$what\" &&\n    --\t\tsed -e \"s/ChangeMe/IWasChanged/\" <\"$what\" >tmp &&\n    --\t\tmv tmp \"$what\"\n    -+\t\tdo_change_me \"$what\"\n    - \t' &&\n    + # check each individual file\n    + for i in $(git -C t4018 ls-files)\n    + do\n    +@@ t/t4018-diff-funcname.sh: do\n      \n    - \ttest_expect_success \"$desc\" '\n    - \t\tgit diff -U1 \"$what\" >diff &&\n    --\t\tsed -n -e \"s/^.*@@$//p\" -e \"s/^.*@@ //p\" <diff >actual &&\n    -+\t\tlast_diff_context_line diff >actual &&\n    - \t\ttest_cmp expected actual\n    - \t' &&\n    + \t\t# add test file to the index\n    + \t\tgit add \\\"$i\\\" &&\n    +-\t\t# place modified file in the worktree\n    +-\t\tsed -e 's/ChangeMe/IWasChanged/' <\\\"t4018/$i.content\\\" >\\\"$i\\\"\n    ++\t\tdo_change_me \\\"$i\\\"\n    + \t\"\n      \n    + \ttest_expect_success \"hunk header: $i\" \"\n    + \t\tgit diff -U1 $i >diff &&\n    +-\t\tsed -n -e 's/^.*@@$//p' -e 's/^.*@@ //p' <diff >ctx &&\n    ++\t\tlast_diff_context_line diff >ctx &&\n    + \t\ttest_cmp t4018/$i.header ctx\n    + \t\"\n    + done\n -:  ----------- > 14:  e64a00d020e userdiff tests: add alternative hunk header test infrastructure\n -:  ----------- > 15:  3dab65bf394 userdiff tests: add a test with multiple tests in a LANG file\n13:  84d20a7cd0c ! 16:  6eff13d01d3 userdiff tests: do config teardown in test_diff_funcname()\n    @@ Commit message\n      ## t/t4018-diff-funcname.sh ##\n     @@ t/t4018-diff-funcname.sh: test_diff_funcname () {\n      \t\tgit diff -U1 \"$what\" >diff &&\n    - \t\tsed -n -e \"s/^.*@@$//p\" -e \"s/^.*@@ //p\" <diff >actual &&\n    + \t\tlast_diff_context_line diff >actual &&\n      \t\ttest_cmp expected actual\n     +\t' &&\n     +\n14:  70fc9fa565b ! 17:  48f15aed4e4 userdiff tests: move custom patterns into one test file\n    @@ Commit message\n     \n      ## t/t4018-diff-funcname.sh ##\n     @@ t/t4018-diff-funcname.sh: test_expect_success 'setup' '\n    - \tbuiltin_drivers=$(test-tool userdiff list-builtin-drivers) &&\n    - \ttest -n \"$builtin_drivers\" &&\n    + \tsort <builtin-drivers >builtin-drivers.sorted &&\n    + \ttest_cmp builtin-drivers.sorted builtin-drivers &&\n      \n     -\t# a non-trivial custom pattern\n     -\tgit config diff.custom1.funcname \"!static\n    @@ t/t4018-diff-funcname.sh: test_expect_success 'setup' '\n     @@ t/t4018-diff-funcname.sh: test_expect_success 'setup' '\n      \n      diffpatterns=\"\n    - \t$builtin_drivers\n    + \t$(cat builtin-drivers)\n     -\tcustom1\n     -\tcustom2\n     -\tcustom3\n15:  8539d6d464e = 18:  11556fe0967 userdiff tests: remove hack for \"RIGHT\" token\n16:  121e5d6dfaf ! 19:  1b96e89843c userdiff tests: do not do compile tests on \"custom\" pattern\n    @@ t/t4018-diff-funcname.sh: test_expect_success 'setup' '\n      '\n      \n     -diffpatterns=\"\n    --\t$builtin_drivers\n    +-\t$(cat builtin-drivers)\n     -\tcustom\n     -\"\n     -\n     -for p in $diffpatterns\n    -+for p in $builtin_drivers\n    ++for p in $(cat builtin-drivers)\n      do\n      \ttest_expect_success \"builtin $p pattern compiles\" '\n      \t\techo \"*.java diff=$p\" >.gitattributes &&\n    -@@ t/t4018-diff-funcname.sh: test_diff_funcname () {\n    - \t'\n    - }\n    +@@ t/t4018-diff-funcname.sh: test_expect_success 'last regexp must not be negated' '\n    + \ttest_i18ngrep \": Last expression must not be negated:\" msg\n    + '\n      \n    --for what in $diffpatterns\n    -+for what in $builtin_drivers custom\n    - do\n    - \ttest=\"$TEST_DIRECTORY/t4018/$what.sh\"\n    - \tif ! test -e \"$test\"\n    ++diffpatterns=\"\n    ++\t$(cat builtin-drivers)\n    ++\tcustom\n    ++\"\n    ++\n    + test_expect_success 'setup hunk header tests' '\n    + \tfor i in $diffpatterns\n    + \tdo\n -:  ----------- > 20:  d3cbfc4354d userdiff tests: assert that new built-in drivers have tests\n17:  451b7ae453d = 21:  fd6c51ac6db userdiff tests + docs: document & test \"diff.<driver>.x?funcname\"\n18:  5a402bb9bf1 = 22:  43d818be785 gitattributes doc: reword discussion of built-in userdiff patterns\n -:  ----------- > 23:  37d54d77755 userdiff tests: move perl tests to perl.sh\n -:  ----------- > 24:  b6f4f613857 userdiff tests: move away from \"RIGHT\" in perl.sh\n19:  a3badb1a3ee ! 25:  a39ba8e2545 gitattributes doc: document multi-line userdiff patterns\n    @@ Documentation/gitattributes.txt: backslashes; the pattern above picks a line tha\n     -There are built-in patterns shipped as part of git itself. A more\n     -advanced version of the `tex` pattern discussed above is one of them.\n     +Multiple patterns can be supplied by listing them one per line\n    -+separated by `\\n`. They will be matched one at a time from left to\n    -+right. Do not supply a trailing \"\\n\" for the last pattern. E.g.:\n    ++separated by `\\n`. They will be matched one line at a time, e.g.:\n     +\n     +------------------------\n     +[diff \"perl\"]\n     +\txfuncname = \"!^=head\\n^[^ ]+.*\"\n     +------------------------\n     +\n    -+Patterns in in a list of multiple that begin with \"!\" are negated. A\n    -+matching negated pattern will cause the matched line to be\n    ++Patterns in a list of multiple patterns that begin with \"!\" are\n    ++negated. A matching negated pattern will cause the matched line to be\n     +skipped. Use it to skip a later pattern that would otherwise match. It\n     +is an error if one or more negated patterns aren't followed by a\n     +non-negated pattern.\n    @@ Documentation/gitattributes.txt: backslashes; the pattern above picks a line tha\n     +construct that will match a literal \"!\" without \"!\" being the first\n     +character on that line, such as \"[!]\".\n     +\n    ++If the last pattern in a list of multiple patterns ends with \"\\n\" it\n    ++will be interpreted as an empty pattern, and will match the first\n    ++empty line. It's almost always a logic error to provide a list of\n    ++multiple patterns ending with \"\\n\", but it's permitted in case you\n    ++genuinely want to match an empty line.\n    ++\n     +If the pattern contains a `$1` capture it will be used instead of the\n     +entire matching line (`$0`) to display the hunk header. This can be\n     +used e.g. to strip whitespace from the beginning of the line, or to\n    @@ t/t4018/custom.sh: ChangeMe\n     +foo\n     +EOF_HUNK\n     +sub foo;\n    ++\n     +=head1\n    ++\n    ++ChangeMe\n    ++\n    ++EOF_TEST\n    ++\n    ++test_expect_success 'custom: multiple patterns ending with \\n' '\n    ++\tgit config diff.custom.xfuncname \"!^=head\n    ++^sub ([^;]+)\n    ++\"\n    ++'\n    ++\n    ++test_diff_funcname 'custom: multiple patterns ending with \\n' \\\n    ++\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n    ++\n    ++EOF_HUNK\n    ++sub foo;\n    ++\n    ++=head1\n    ++\n     +ChangeMe\n     +\n     +EOF_TEST\n     \n      ## t/t4018/perl.sh ##\n    -@@ t/t4018/perl.sh: sub RIGHT\n    +@@ t/t4018/perl.sh: sub asub\n      \tprint \"ChangeMe\\n\";\n      }\n      EOF_TEST\n    @@ t/t4018/perl.sh: sub RIGHT\n     +EOF_HUNK\n     +sub foo;\n     +=head1\n    -+ChangeMe\n     +\n    ++ChangeMe\n     +EOF_TEST\n -:  ----------- > 26:  27394c6c2a4 userdiff tests: switch to -U0 by default\n20:  1b46726e85f = 27:  91ab863a298 userdiff tests: remove \"funcname\" from custom3 test\n22:  24548fb680e <  -:  ----------- userdiff tests: test hunk headers on accumulated files\n23:  48f00a59d5e <  -:  ----------- userdiff tests: test hunk header selection with -U0\n24:  05a01990c9c ! 28:  b68133ce5f7 userdiff tests: assert empty hunk header context on -U<large>\n    @@ Commit message\n     \n         Assert the existing behavior that under -U<large> we'll show no hunk\n         header context, where <large> takes us past the potential hunk header\n    -    we'd have extracted. I'm just picking a number over nine thousand as a\n    -    really large number we're unlikely to exceed in these tests.\n    +    we'd have extracted.\n    +\n    +    I'm just picking a number over nine thousand as a really large number\n    +    we're unlikely to exceed in these tests.\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n    @@ t/t4018-diff-funcname.sh: test_diff_funcname () {\n      \t\ttest_cmp expected actual\n      \t' &&\n      \n    -+\ttest_expect_success \"$desc -U9001 (accumulated)\" '\n    -+\t\tgit diff -U9001 \"$what\".acc >diff &&\n    -+\t\tlast_diff_context_line diff >actual.lines &&\n    -+\t\ttail -n 1 actual.lines >actual &&\n    ++\ttest_expect_success \"$desc -U9001\" '\n    ++\t\tgit diff -U9001 \"$what\" >diff &&\n    ++\t\tlast_diff_context_line diff >actual &&\n     +\t\techo >blank &&\n     +\t\ttest_cmp blank actual\n     +\t' &&\n25:  3d2f42d7041 <  -:  ----------- userdiff: match \"package\" in diff=golang\n -:  ----------- > 29:  9f3a7ca788b userdiff tests: test for a bug in 1dbf0c0ad6c\n -:  ----------- > 30:  43ee24e554b userdiff golang: simplify and correct matching regex\n -:  ----------- > 31:  70a2e7ca70b userdiff golang: don't over-match intented constructs\n -:  ----------- > 32:  6b942cd651b userdiff golang: add a rule to match \"package\"\n -:  ----------- > 33:  f45d35387d9 userdiff golang: match multi-line \"const\" and \"import\"\n26:  b2e16ade06c ! 34:  c67c3e160f3 userdiff tests: add basic test for ada\n    @@ Commit message\n         1. https://rosettacode.org/wiki/99_bottles_of_beer#Ada\n         2. https://en.wikibooks.org/wiki/Ada_Programming/Tasking\n     \n    + ## t/t4018-diff-funcname.sh ##\n    +@@ t/t4018-diff-funcname.sh: test_expect_success 'setup' '\n    + \t# Do not add anything to this list. New built-in drivers should have\n    + \t# tests\n    + \tcat >drivers-no-tests <<-\\EOF &&\n    +-\tada\n    + \tbibtex\n    + \tcsharp\n    + \thtml\n    +\n      ## t/t4018/ada.sh (new) ##\n     @@\n     +#!/bin/sh\n27:  826b6f4d6ae ! 35:  e2aedd738ef userdiff tests: add basic test for ruby\n    @@ Commit message\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n    + ## t/t4018-diff-funcname.sh ##\n    +@@ t/t4018-diff-funcname.sh: test_expect_success 'setup' '\n    + \thtml\n    + \tobjc\n    + \tpascal\n    +-\truby\n    + \ttex\n    + \tEOF\n    + \n    +\n      ## t/t4018/ruby.sh (new) ##\n     @@\n     +#!/bin/sh\n    @@ t/t4018/ruby.sh (new)\n     +EOF_TEST\n     +\n     +test_diff_funcname 'ruby: picks first \"class/module/def\" before changed context' \\\n    -+\t\"class Two\" \\\n    ++\t'-U1' \\\n     +\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n     +class One\n     +EOF_HUNK\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"417718","messageId":"20210224195129.4004-2-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"[PATCH v3 01/35] userdiff: refactor away the parse_bool() function","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-24T19:50:55Z","receivedAt":"2021-02-24T19:52:58Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Since 6680a0874f (drop odd return value semantics from\nuserdiff_config, 2012-02-07) we have not cared about the return values\nof parse_tristate() or git_config_bool() v.s. falling through in\nuserdiff_config(), so let's do so in those cases to make the code\neasier to read.\n\nHaving a wrapper function for git_config_bool() dates back to\nd9bae1a178 (diff: cache textconv output, 2010-04-01) and\n122aa6f9c0 (diff: introduce diff.<driver>.binary, 2008-10-05), both of\nwhich predated the change in 6680a0874f which made their return values\nredundant.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n userdiff.c | 18 ++++++------------\n 1 file changed, 6 insertions(+), 12 deletions(-)\n\ndiff --git a/userdiff.c b/userdiff.c\nindex 3f81a2261c5..c147bcbb173 100644\n--- a/userdiff.c\n+++ b/userdiff.c\n@@ -275,19 +275,12 @@ static int parse_funcname(struct userdiff_funcname *f, const char *k,\n \treturn 0;\n }\n \n-static int parse_tristate(int *b, const char *k, const char *v)\n+static void parse_tristate(int *b, const char *k, const char *v)\n {\n \tif (v && !strcasecmp(v, \"auto\"))\n \t\t*b = -1;\n \telse\n \t\t*b = git_config_bool(k, v);\n-\treturn 0;\n-}\n-\n-static int parse_bool(int *b, const char *k, const char *v)\n-{\n-\t*b = git_config_bool(k, v);\n-\treturn 0;\n }\n \n int userdiff_config(const char *k, const char *v)\n@@ -312,16 +305,17 @@ int userdiff_config(const char *k, const char *v)\n \t\treturn parse_funcname(&drv->funcname, k, v, 0);\n \tif (!strcmp(type, \"xfuncname\"))\n \t\treturn parse_funcname(&drv->funcname, k, v, REG_EXTENDED);\n-\tif (!strcmp(type, \"binary\"))\n-\t\treturn parse_tristate(&drv->binary, k, v);\n \tif (!strcmp(type, \"command\"))\n \t\treturn git_config_string(&drv->external, k, v);\n \tif (!strcmp(type, \"textconv\"))\n \t\treturn git_config_string(&drv->textconv, k, v);\n-\tif (!strcmp(type, \"cachetextconv\"))\n-\t\treturn parse_bool(&drv->textconv_want_cache, k, v);\n \tif (!strcmp(type, \"wordregex\"))\n \t\treturn git_config_string(&drv->word_regex, k, v);\n+\t/* Don't care about the parse errors for these, fallthrough */\n+\tif (!strcmp(type, \"cachetextconv\"))\n+\t\tdrv->textconv_want_cache = git_config_bool(k, v);\n+\tif (!strcmp(type, \"binary\"))\n+\t\tparse_tristate(&drv->binary, k, v);\n \n \treturn 0;\n }\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"417720","messageId":"20210224195129.4004-3-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"[PATCH v3 02/35] userdiff style: re-order drivers in alphabetical order","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-24T19:50:56Z","receivedAt":"2021-02-24T19:52:58Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Address some old code smell and move around the built-in userdiff\ndrivers so they're both in alphabetical order, and now in the same\norder they appear in the gitattributes(5) documentation.\n\nThe two started drifting in be58e70dba (diff: unify external diff and\nfuncname parsing code, 2008-10-05), and then even further in\n80c49c3de2 (color-words: make regex configurable via attributes,\n2009-01-17) when the \"cpp\" pattern was added.\n\nThere are no functional changes here, and as --color-moved will show\nonly moved existing lines.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n userdiff.c | 76 +++++++++++++++++++++++++++---------------------------\n 1 file changed, 38 insertions(+), 38 deletions(-)\n\ndiff --git a/userdiff.c b/userdiff.c\nindex c147bcbb173..c92cbcc0540 100644\n--- a/userdiff.c\n+++ b/userdiff.c\n@@ -44,6 +44,44 @@ PATTERNS(\"bash\",\n \t /* -- */\n \t /* Characters not in the default $IFS value */\n \t \"[^ \\t]+\"),\n+PATTERNS(\"bibtex\", \"(@[a-zA-Z]{1,}[ \\t]*\\\\{{0,1}[ \\t]*[^ \\t\\\"@',\\\\#}{~%]*).*$\",\n+\t \"[={}\\\"]|[^={}\\\" \\t]+\"),\n+PATTERNS(\"cpp\",\n+\t /* Jump targets or access declarations */\n+\t \"!^[ \\t]*[A-Za-z_][A-Za-z_0-9]*:[[:space:]]*($|/[/*])\\n\"\n+\t /* functions/methods, variables, and compounds at top level */\n+\t \"^((::[[:space:]]*)?[A-Za-z_].*)$\",\n+\t /* -- */\n+\t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n+\t \"|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lLuU]*\"\n+\t \"|[-+*/<>%&^|=!]=|--|\\\\+\\\\+|<<=?|>>=?|&&|\\\\|\\\\||::|->\\\\*?|\\\\.\\\\*\"),\n+PATTERNS(\"csharp\",\n+\t /* Keywords */\n+\t \"!^[ \\t]*(do|while|for|if|else|instanceof|new|return|switch|case|throw|catch|using)\\n\"\n+\t /* Methods and constructors */\n+\t \"^[ \\t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe|async)[ \\t]+)*[][<>@.~_[:alnum:]]+[ \\t]+[<>@._[:alnum:]]+[ \\t]*\\\\(.*\\\\))[ \\t]*$\\n\"\n+\t /* Properties */\n+\t \"^[ \\t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \\t]+)*[][<>@.~_[:alnum:]]+[ \\t]+[@._[:alnum:]]+)[ \\t]*$\\n\"\n+\t /* Type definitions */\n+\t \"^[ \\t]*(((static|public|internal|private|protected|new|unsafe|sealed|abstract|partial)[ \\t]+)*(class|enum|interface|struct)[ \\t]+.*)$\\n\"\n+\t /* Namespace */\n+\t \"^[ \\t]*(namespace[ \\t]+.*)$\",\n+\t /* -- */\n+\t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n+\t \"|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?\"\n+\t \"|[-+*/<>%&^|=!]=|--|\\\\+\\\\+|<<=?|>>=?|&&|\\\\|\\\\||::|->\"),\n+IPATTERN(\"css\",\n+\t \"![:;][[:space:]]*$\\n\"\n+\t \"^[:[@.#]?[_a-z0-9].*$\",\n+\t /* -- */\n+\t /*\n+\t  * This regex comes from W3C CSS specs. Should theoretically also\n+\t  * allow ISO 10646 characters U+00A0 and higher,\n+\t  * but they are not handled in this regex.\n+\t  */\n+\t \"-?[_a-zA-Z][-_a-zA-Z0-9]*\" /* identifiers */\n+\t \"|-?[0-9]+|\\\\#[0-9a-fA-F]+\" /* numbers */\n+),\n PATTERNS(\"dts\",\n \t \"!;\\n\"\n \t \"!=\\n\"\n@@ -191,46 +229,8 @@ PATTERNS(\"rust\",\n \t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n \t \"|[0-9][0-9_a-fA-Fiosuxz]*(\\\\.([0-9]*[eE][+-]?)?[0-9_fF]*)?\"\n \t \"|[-+*\\\\/<>%&^|=!:]=|<<=?|>>=?|&&|\\\\|\\\\||->|=>|\\\\.{2}=|\\\\.{3}|::\"),\n-PATTERNS(\"bibtex\", \"(@[a-zA-Z]{1,}[ \\t]*\\\\{{0,1}[ \\t]*[^ \\t\\\"@',\\\\#}{~%]*).*$\",\n-\t \"[={}\\\"]|[^={}\\\" \\t]+\"),\n PATTERNS(\"tex\", \"^(\\\\\\\\((sub)*section|chapter|part)\\\\*{0,1}\\\\{.*)$\",\n \t \"\\\\\\\\[a-zA-Z@]+|\\\\\\\\.|[a-zA-Z0-9\\x80-\\xff]+\"),\n-PATTERNS(\"cpp\",\n-\t /* Jump targets or access declarations */\n-\t \"!^[ \\t]*[A-Za-z_][A-Za-z_0-9]*:[[:space:]]*($|/[/*])\\n\"\n-\t /* functions/methods, variables, and compounds at top level */\n-\t \"^((::[[:space:]]*)?[A-Za-z_].*)$\",\n-\t /* -- */\n-\t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n-\t \"|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lLuU]*\"\n-\t \"|[-+*/<>%&^|=!]=|--|\\\\+\\\\+|<<=?|>>=?|&&|\\\\|\\\\||::|->\\\\*?|\\\\.\\\\*\"),\n-PATTERNS(\"csharp\",\n-\t /* Keywords */\n-\t \"!^[ \\t]*(do|while|for|if|else|instanceof|new|return|switch|case|throw|catch|using)\\n\"\n-\t /* Methods and constructors */\n-\t \"^[ \\t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe|async)[ \\t]+)*[][<>@.~_[:alnum:]]+[ \\t]+[<>@._[:alnum:]]+[ \\t]*\\\\(.*\\\\))[ \\t]*$\\n\"\n-\t /* Properties */\n-\t \"^[ \\t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \\t]+)*[][<>@.~_[:alnum:]]+[ \\t]+[@._[:alnum:]]+)[ \\t]*$\\n\"\n-\t /* Type definitions */\n-\t \"^[ \\t]*(((static|public|internal|private|protected|new|unsafe|sealed|abstract|partial)[ \\t]+)*(class|enum|interface|struct)[ \\t]+.*)$\\n\"\n-\t /* Namespace */\n-\t \"^[ \\t]*(namespace[ \\t]+.*)$\",\n-\t /* -- */\n-\t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n-\t \"|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?\"\n-\t \"|[-+*/<>%&^|=!]=|--|\\\\+\\\\+|<<=?|>>=?|&&|\\\\|\\\\||::|->\"),\n-IPATTERN(\"css\",\n-\t \"![:;][[:space:]]*$\\n\"\n-\t \"^[:[@.#]?[_a-z0-9].*$\",\n-\t /* -- */\n-\t /*\n-\t  * This regex comes from W3C CSS specs. Should theoretically also\n-\t  * allow ISO 10646 characters U+00A0 and higher,\n-\t  * but they are not handled in this regex.\n-\t  */\n-\t \"-?[_a-zA-Z][-_a-zA-Z0-9]*\" /* identifiers */\n-\t \"|-?[0-9]+|\\\\#[0-9a-fA-F]+\" /* numbers */\n-),\n { \"default\", NULL, -1, { NULL, 0 } },\n };\n #undef PATTERNS\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"417719","messageId":"20210224195129.4004-4-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"[PATCH v3 03/35] userdiff style: declare patterns with consistent style","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-24T19:50:57Z","receivedAt":"2021-02-24T19:52:59Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change those patterns which were declared with a regex on the same\nline as the \"PATTERNS()\" line to put that regex on the next line, and\nadd missing \"/* -- */\" separator comments between the pattern and\nword_regex.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n userdiff.c | 20 +++++++++++++++-----\n 1 file changed, 15 insertions(+), 5 deletions(-)\n\ndiff --git a/userdiff.c b/userdiff.c\nindex c92cbcc0540..c7aaf7094f8 100644\n--- a/userdiff.c\n+++ b/userdiff.c\n@@ -44,7 +44,9 @@ PATTERNS(\"bash\",\n \t /* -- */\n \t /* Characters not in the default $IFS value */\n \t \"[^ \\t]+\"),\n-PATTERNS(\"bibtex\", \"(@[a-zA-Z]{1,}[ \\t]*\\\\{{0,1}[ \\t]*[^ \\t\\\"@',\\\\#}{~%]*).*$\",\n+PATTERNS(\"bibtex\",\n+\t \"(@[a-zA-Z]{1,}[ \\t]*\\\\{{0,1}[ \\t]*[^ \\t\\\"@',\\\\#}{~%]*).*$\",\n+\t /* -- */\n \t \"[={}\\\"]|[^={}\\\" \\t]+\"),\n PATTERNS(\"cpp\",\n \t /* Jump targets or access declarations */\n@@ -121,7 +123,9 @@ IPATTERN(\"fortran\",\n \t  * they would have been matched above as a variable anyway. */\n \t \"|[-+]?[0-9.]+([AaIiDdEeFfLlTtXx][Ss]?[-+]?[0-9.]*)?(_[a-zA-Z0-9][a-zA-Z0-9_]*)?\"\n \t \"|//|\\\\*\\\\*|::|[/<>=]=\"),\n-IPATTERN(\"fountain\", \"^((\\\\.[^.]|(int|ext|est|int\\\\.?/ext|i/e)[. ]).*)$\",\n+IPATTERN(\"fountain\",\n+\t \"^((\\\\.[^.]|(int|ext|est|int\\\\.?/ext|i/e)[. ]).*)$\",\n+\t /* -- */\n \t \"[^ \\t-]+\"),\n PATTERNS(\"golang\",\n \t /* Functions */\n@@ -132,7 +136,9 @@ PATTERNS(\"golang\",\n \t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n \t \"|[-+0-9.eE]+i?|0[xX]?[0-9a-fA-F]+i?\"\n \t \"|[-+*/<>%&^|=!:]=|--|\\\\+\\\\+|<<=?|>>=?|&\\\\^=?|&&|\\\\|\\\\||<-|\\\\.{3}\"),\n-PATTERNS(\"html\", \"^[ \\t]*(<[Hh][1-6]([ \\t].*)?>.*)$\",\n+PATTERNS(\"html\",\n+\t \"^[ \\t]*(<[Hh][1-6]([ \\t].*)?>.*)$\",\n+\t /* -- */\n \t \"[^<>= \\t]+\"),\n PATTERNS(\"java\",\n \t \"!^[ \\t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)\\n\"\n@@ -144,6 +150,7 @@ PATTERNS(\"java\",\n \t \"|--|\\\\+\\\\+|<<=?|>>>?=?|&&|\\\\|\\\\|\"),\n PATTERNS(\"markdown\",\n \t \"^ {0,3}#{1,6}[ \\t].*\",\n+\t /* -- */\n \t \"[^<>= \\t]+\"),\n PATTERNS(\"matlab\",\n \t /*\n@@ -152,6 +159,7 @@ PATTERNS(\"matlab\",\n \t  * that is understood by both.\n \t  */\n \t \"^[[:space:]]*((classdef|function)[[:space:]].*)$|^(%%%?|##)[[:space:]].*$\",\n+\t /* -- */\n \t \"[a-zA-Z_][a-zA-Z0-9_]*|[-+0-9.e]+|[=~<>]=|\\\\.[*/\\\\^']|\\\\|\\\\||&&\"),\n PATTERNS(\"objc\",\n \t /* Negate C statements that can look like functions */\n@@ -212,13 +220,15 @@ PATTERNS(\"php\",\n \t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n \t \"|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+\"\n \t \"|[-+*/<>%&^|=!.]=|--|\\\\+\\\\+|<<=?|>>=?|===|&&|\\\\|\\\\||::|->\"),\n-PATTERNS(\"python\", \"^[ \\t]*((class|(async[ \\t]+)?def)[ \\t].*)$\",\n+PATTERNS(\"python\",\n+\t \"^[ \\t]*((class|(async[ \\t]+)?def)[ \\t].*)$\",\n \t /* -- */\n \t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n \t \"|[-+0-9.e]+[jJlL]?|0[xX]?[0-9a-fA-F]+[lL]?\"\n \t \"|[-+*/<>%&^|=!]=|//=?|<<=?|>>=?|\\\\*\\\\*=?\"),\n \t /* -- */\n-PATTERNS(\"ruby\", \"^[ \\t]*((class|module|def)[ \\t].*)$\",\n+PATTERNS(\"ruby\",\n+\t \"^[ \\t]*((class|module|def)[ \\t].*)$\",\n \t /* -- */\n \t \"(@|@@|\\\\$)?[a-zA-Z_][a-zA-Z0-9_]*\"\n \t \"|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+|\\\\?(\\\\\\\\C-)?(\\\\\\\\M-)?.\"\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"417721","messageId":"20210224195129.4004-5-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"[PATCH v3 04/35] userdiff style: normalize pascal regex declaration","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-24T19:50:58Z","receivedAt":"2021-02-24T19:53:22Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Declare the pascal pattern consistently with how we declare the\nothers, not having \"\\n\" on one line by itself, but as part of the\npattern, and when there are alterations have the \"|\" at the start, not\nend of the line.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n userdiff.c | 5 ++---\n 1 file changed, 2 insertions(+), 3 deletions(-)\n\ndiff --git a/userdiff.c b/userdiff.c\nindex c7aaf7094f8..10a02d36209 100644\n--- a/userdiff.c\n+++ b/userdiff.c\n@@ -175,9 +175,8 @@ PATTERNS(\"objc\",\n \t \"|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?\"\n \t \"|[-+*/<>%&^|=!]=|--|\\\\+\\\\+|<<=?|>>=?|&&|\\\\|\\\\||::|->\"),\n PATTERNS(\"pascal\",\n-\t \"^(((class[ \\t]+)?(procedure|function)|constructor|destructor|interface|\"\n-\t\t\"implementation|initialization|finalization)[ \\t]*.*)$\"\n-\t \"\\n\"\n+\t \"^(((class[ \\t]+)?(procedure|function)|constructor|destructor|interface\"\n+\t \"|implementation|initialization|finalization)[ \\t]*.*)$\\n\"\n \t \"^(.*=[ \\t]*(class|record).*)$\",\n \t /* -- */\n \t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"417722","messageId":"20210224195129.4004-6-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"[PATCH v3 05/35] userdiff: add and use for_each_userdiff_driver()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-24T19:50:59Z","receivedAt":"2021-02-24T19:53:24Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Refactor the userdiff_find_by_namelen() function so that a new\nfor_each_userdiff_driver() API function does most of the work.\n\nThis will be useful for the same reason we've got other for_each_*()\nAPI functions as part of various APIs, and will be used in a follow-up\ncommit.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n userdiff.c | 61 +++++++++++++++++++++++++++++++++++++++++++-----------\n userdiff.h | 15 ++++++++++++++\n 2 files changed, 64 insertions(+), 12 deletions(-)\n\ndiff --git a/userdiff.c b/userdiff.c\nindex 10a02d36209..55f4f769bd3 100644\n--- a/userdiff.c\n+++ b/userdiff.c\n@@ -259,20 +259,32 @@ static struct userdiff_driver driver_false = {\n \t{ NULL, 0 }\n };\n \n-static struct userdiff_driver *userdiff_find_by_namelen(const char *k, size_t len)\n+struct for_each_userdiff_driver_cb {\n+\tconst char *k;\n+\tsize_t len;\n+\tstruct userdiff_driver *driver;\n+};\n+\n+static int userdiff_find_by_namelen_cb(struct userdiff_driver *driver,\n+\t\t\t\t       enum userdiff_driver_type type, void *priv)\n {\n-\tint i;\n-\tfor (i = 0; i < ndrivers; i++) {\n-\t\tstruct userdiff_driver *drv = drivers + i;\n-\t\tif (!strncmp(drv->name, k, len) && !drv->name[len])\n-\t\t\treturn drv;\n-\t}\n-\tfor (i = 0; i < ARRAY_SIZE(builtin_drivers); i++) {\n-\t\tstruct userdiff_driver *drv = builtin_drivers + i;\n-\t\tif (!strncmp(drv->name, k, len) && !drv->name[len])\n-\t\t\treturn drv;\n+\tstruct for_each_userdiff_driver_cb *cb_data = priv;\n+\n+\tif (!strncmp(driver->name, cb_data->k, cb_data->len) &&\n+\t    !driver->name[cb_data->len]) {\n+\t\tcb_data->driver = driver;\n+\t\treturn -1; /* found it! */\n \t}\n-\treturn NULL;\n+\treturn 0;\n+}\n+\n+static struct userdiff_driver *userdiff_find_by_namelen(const char *k, size_t len)\n+{\n+\tstruct for_each_userdiff_driver_cb udcbdata = { .k = k, .len = len, .driver = NULL };\n+\n+\tfor_each_userdiff_driver(userdiff_find_by_namelen_cb,\n+\t\t\t\t USERDIFF_DRIVER_TYPE_UNSPECIFIED, &udcbdata);\n+\treturn udcbdata.driver;\n }\n \n static int parse_funcname(struct userdiff_funcname *f, const char *k,\n@@ -373,3 +385,28 @@ struct userdiff_driver *userdiff_get_textconv(struct repository *r,\n \n \treturn driver;\n }\n+\n+int for_each_userdiff_driver(each_userdiff_driver_fn fn,\n+\t\t\t     enum userdiff_driver_type type, void *cb_data)\n+{\n+\tint i, ret;\n+\tif (type & (USERDIFF_DRIVER_TYPE_UNSPECIFIED | USERDIFF_DRIVER_TYPE_CUSTOM)) {\n+\n+\t\tfor (i = 0; i < ndrivers; i++) {\n+\t\t\tstruct userdiff_driver *drv = drivers + i;\n+\t\t\tret = fn(drv, USERDIFF_DRIVER_TYPE_CUSTOM, cb_data);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\t\t}\n+\t}\n+\tif (type & (USERDIFF_DRIVER_TYPE_UNSPECIFIED | USERDIFF_DRIVER_TYPE_BUILTIN)) {\n+\n+\t\tfor (i = 0; i < ARRAY_SIZE(builtin_drivers); i++) {\n+\t\t\tstruct userdiff_driver *drv = builtin_drivers + i;\n+\t\t\tret = fn(drv, USERDIFF_DRIVER_TYPE_BUILTIN, cb_data);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\t\t}\n+\t}\n+\treturn 0;\n+}\ndiff --git a/userdiff.h b/userdiff.h\nindex 203057e13e5..fe14014a775 100644\n--- a/userdiff.h\n+++ b/userdiff.h\n@@ -21,6 +21,13 @@ struct userdiff_driver {\n \tstruct notes_cache *textconv_cache;\n \tint textconv_want_cache;\n };\n+enum userdiff_driver_type {\n+\tUSERDIFF_DRIVER_TYPE_UNSPECIFIED = 1<<0,\n+\tUSERDIFF_DRIVER_TYPE_BUILTIN = 1<<1,\n+\tUSERDIFF_DRIVER_TYPE_CUSTOM = 1<<2,\n+};\n+typedef int (*each_userdiff_driver_fn)(struct userdiff_driver *,\n+\t\t\t\t       enum userdiff_driver_type, void *);\n \n int userdiff_config(const char *k, const char *v);\n struct userdiff_driver *userdiff_find_by_name(const char *name);\n@@ -34,4 +41,12 @@ struct userdiff_driver *userdiff_find_by_path(struct index_state *istate,\n struct userdiff_driver *userdiff_get_textconv(struct repository *r,\n \t\t\t\t\t      struct userdiff_driver *driver);\n \n+/*\n+ * Iterate over each driver of type userdiff_driver_type, or\n+ * USERDIFF_DRIVER_TYPE_UNSPECIFIED for all of them. Return non-zero\n+ * to exit from the loop.\n+ */\n+int for_each_userdiff_driver(each_userdiff_driver_fn,\n+\t\t\t     enum userdiff_driver_type, void *);\n+\n #endif /* USERDIFF */\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"417723","messageId":"20210224195129.4004-7-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"[PATCH v3 06/35] userdiff tests: explicitly test \"default\" pattern","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-24T19:51:00Z","receivedAt":"2021-02-24T19:53:43Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Since 122aa6f9c0 (diff: introduce diff.<driver>.binary, 2008-10-05)\nthe internals of the userdiff.c code have understood a \"default\" name,\nwhich is invoked as userdiff_find_by_name(\"default\") and present in\nthe \"builtin_drivers\" struct. Let's test for this special case.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018-diff-funcname.sh | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex 9675bc17db2..cefe329aea7 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -49,6 +49,7 @@ diffpatterns=\"\n \truby\n \trust\n \ttex\n+\tdefault\n \tcustom1\n \tcustom2\n \tcustom3\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"417724","messageId":"20210224195129.4004-8-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"[PATCH v3 07/35] userdiff tests: list builtin drivers via test-tool","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-24T19:51:01Z","receivedAt":"2021-02-24T19:53:46Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the userdiff test to list the builtin drivers via the\ntest-tool, using the new for_each_userdiff_driver() API function.\n\nThis gets rid of the need to modify this part of the test every time a\nnew pattern is added, see 2ff6c34612 (userdiff: support Bash,\n2020-10-22) and 09dad9256a (userdiff: support Markdown, 2020-05-02)\nfor two recent examples.\n\nI only need the \"list-builtin-drivers \"argument here, but let's add\n\"list-custom-drivers\" and \"list-drivers\" too, just because it's easy.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Makefile                 |  1 +\n t/helper/test-tool.c     |  1 +\n t/helper/test-tool.h     |  1 +\n t/helper/test-userdiff.c | 31 +++++++++++++++++++++++++++++++\n t/t4018-diff-funcname.sh | 32 ++++++++------------------------\n 5 files changed, 42 insertions(+), 24 deletions(-)\n create mode 100644 t/helper/test-userdiff.c\n\ndiff --git a/Makefile b/Makefile\nindex 5a239cac20e..710a0deaed0 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -741,6 +741,7 @@ TEST_BUILTINS_OBJS += test-submodule-nested-repo-config.o\n TEST_BUILTINS_OBJS += test-subprocess.o\n TEST_BUILTINS_OBJS += test-trace2.o\n TEST_BUILTINS_OBJS += test-urlmatch-normalization.o\n+TEST_BUILTINS_OBJS += test-userdiff.o\n TEST_BUILTINS_OBJS += test-wildmatch.o\n TEST_BUILTINS_OBJS += test-windows-named-pipe.o\n TEST_BUILTINS_OBJS += test-write-cache.o\ndiff --git a/t/helper/test-tool.c b/t/helper/test-tool.c\nindex f97cd9f48a6..dcb05ca6e5e 100644\n--- a/t/helper/test-tool.c\n+++ b/t/helper/test-tool.c\n@@ -71,6 +71,7 @@ static struct test_cmd cmds[] = {\n \t{ \"submodule-nested-repo-config\", cmd__submodule_nested_repo_config },\n \t{ \"subprocess\", cmd__subprocess },\n \t{ \"trace2\", cmd__trace2 },\n+\t{ \"userdiff\", cmd__userdiff },\n \t{ \"urlmatch-normalization\", cmd__urlmatch_normalization },\n \t{ \"xml-encode\", cmd__xml_encode },\n \t{ \"wildmatch\", cmd__wildmatch },\ndiff --git a/t/helper/test-tool.h b/t/helper/test-tool.h\nindex 28072c0ad5a..589f2e8ac67 100644\n--- a/t/helper/test-tool.h\n+++ b/t/helper/test-tool.h\n@@ -61,6 +61,7 @@ int cmd__submodule_config(int argc, const char **argv);\n int cmd__submodule_nested_repo_config(int argc, const char **argv);\n int cmd__subprocess(int argc, const char **argv);\n int cmd__trace2(int argc, const char **argv);\n+int cmd__userdiff(int argc, const char **argv);\n int cmd__urlmatch_normalization(int argc, const char **argv);\n int cmd__xml_encode(int argc, const char **argv);\n int cmd__wildmatch(int argc, const char **argv);\ndiff --git a/t/helper/test-userdiff.c b/t/helper/test-userdiff.c\nnew file mode 100644\nindex 00000000000..f173a4f18af\n--- /dev/null\n+++ b/t/helper/test-userdiff.c\n@@ -0,0 +1,31 @@\n+#include \"test-tool.h\"\n+#include \"cache.h\"\n+#include \"userdiff.h\"\n+\n+static int driver_cb(struct userdiff_driver *driver,\n+\t\t     enum userdiff_driver_type type, void *priv)\n+{\n+\tif (driver->funcname.pattern)\n+\t\tputs(driver->name);\n+\treturn 0;\n+}\n+\n+static int list_what(enum userdiff_driver_type type)\n+{\n+\treturn for_each_userdiff_driver(driver_cb, type, NULL);\n+}\n+\n+int cmd__userdiff(int argc, const char **argv)\n+{\n+\tif (argc != 2)\n+\t\treturn 1;\n+\n+\tif (!strcmp(argv[1], \"list-drivers\"))\n+\t\treturn list_what(USERDIFF_DRIVER_TYPE_UNSPECIFIED);\n+\telse if (!strcmp(argv[1], \"list-builtin-drivers\"))\n+\t\treturn list_what(USERDIFF_DRIVER_TYPE_BUILTIN);\n+\telse if (!strcmp(argv[1], \"list-custom-drivers\"))\n+\t\treturn list_what(USERDIFF_DRIVER_TYPE_CUSTOM);\n+\telse\n+\t\treturn error(\"unknown argument %s\", argv[1]);\n+}\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex cefe329aea7..5bd82e09ab3 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -8,6 +8,13 @@ test_description='Test custom diff function name patterns'\n . ./test-lib.sh\n \n test_expect_success 'setup' '\n+\t# Make sure additions to builtin_drivers are sorted\n+\ttest_when_finished \"rm builtin-drivers.sorted\" &&\n+\ttest-tool userdiff list-builtin-drivers >builtin-drivers &&\n+\ttest_file_not_empty builtin-drivers &&\n+\tsort <builtin-drivers >builtin-drivers.sorted &&\n+\ttest_cmp builtin-drivers.sorted builtin-drivers &&\n+\n \t# a non-trivial custom pattern\n \tgit config diff.custom1.funcname \"!static\n !String\n@@ -26,30 +33,7 @@ test_expect_success 'setup' '\n '\n \n diffpatterns=\"\n-\tada\n-\tbash\n-\tbibtex\n-\tcpp\n-\tcsharp\n-\tcss\n-\tdts\n-\telixir\n-\tfortran\n-\tfountain\n-\tgolang\n-\thtml\n-\tjava\n-\tmarkdown\n-\tmatlab\n-\tobjc\n-\tpascal\n-\tperl\n-\tphp\n-\tpython\n-\truby\n-\trust\n-\ttex\n-\tdefault\n+\t$(cat builtin-drivers)\n \tcustom1\n \tcustom2\n \tcustom3\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"417725","messageId":"20210224195129.4004-9-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"[PATCH v3 08/35] userdiff: remove support for \"broken\" tests","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-24T19:51:02Z","receivedAt":"2021-02-24T19:54:08Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"There have been no \"broken\" tests since 75c3b6b2e8 (userdiff: improve\nFortran xfuncname regex, 2020-08-12). Let's remove the test support\nfor them, this is in preparation for a more general refactoring of the\ntests.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018-diff-funcname.sh | 8 +-------\n t/t4018/README           | 3 ---\n 2 files changed, 1 insertion(+), 10 deletions(-)\n\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex 5bd82e09ab3..9aec9f8e6de 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -86,13 +86,7 @@ test_expect_success 'setup hunk header tests' '\n # check each individual file\n for i in $(git ls-files)\n do\n-\tif grep broken \"$i\" >/dev/null 2>&1\n-\tthen\n-\t\tresult=failure\n-\telse\n-\t\tresult=success\n-\tfi\n-\ttest_expect_$result \"hunk header: $i\" \"\n+\ttest_expect_success \"hunk header: $i\" \"\n \t\tgit diff -U1 $i >actual &&\n \t\tgrep '@@ .* @@.*RIGHT' actual\n \t\"\ndiff --git a/t/t4018/README b/t/t4018/README\nindex 283e01cca1a..2d25b2b4fc9 100644\n--- a/t/t4018/README\n+++ b/t/t4018/README\n@@ -7,9 +7,6 @@ at least two lines from the line that must appear in the hunk header.\n The text that must appear in the hunk header must contain the word\n \"right\", but in all upper-case, like in the title above.\n \n-To mark a test case that highlights a malfunction, insert the word\n-BROKEN in all lower-case somewhere in the file.\n-\n This text is a bit twisted and out of order, but it is itself a\n test case for the default hunk header pattern. Know what you are doing\n if you change it.\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"417726","messageId":"20210224195129.4004-10-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"[PATCH v3 09/35] blame tests: don't rely on t/t4018/ directory","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-24T19:51:03Z","receivedAt":"2021-02-24T19:54:08Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Refactor a test added in 9466e3809d (blame: enable funcname blaming\nwith userdiff driver, 2020-11-01) so that the blame tests don't rely\non stealing the contents of \"t/t4018/fortran-external-function\".\n\nI'm about to change that file in a subsequent commit. Just moving the\nrelevant test file here inline is the easiest solution, and I think\nalso the most readable.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/annotate-tests.sh | 16 +++++++++++++---\n 1 file changed, 13 insertions(+), 3 deletions(-)\n\ndiff --git a/t/annotate-tests.sh b/t/annotate-tests.sh\nindex 29ce89090d8..04a2c58594c 100644\n--- a/t/annotate-tests.sh\n+++ b/t/annotate-tests.sh\n@@ -482,12 +482,22 @@ test_expect_success 'blame -L ^:RE (absolute: end-of-file)' '\n test_expect_success 'setup -L :funcname with userdiff driver' '\n \techo \"fortran-* diff=fortran\" >.gitattributes &&\n \tfortran_file=fortran-external-function &&\n-\torig_file=\"$TEST_DIRECTORY/t4018/$fortran_file\" &&\n-\tcp \"$orig_file\" . &&\n+\tcat >$fortran_file <<-\\EOF &&\n+\tfunction RIGHT(a, b) result(c)\n+\n+\tinteger, intent(in) :: ChangeMe\n+\tinteger, intent(in) :: b\n+\tinteger, intent(out) :: c\n+\n+\tc = a+b\n+\n+\tend function RIGHT\n+\tEOF\n \tgit add \"$fortran_file\" &&\n \tGIT_AUTHOR_NAME=\"A\" GIT_AUTHOR_EMAIL=\"A@test.git\" \\\n \tgit commit -m \"add fortran file\" &&\n-\tsed -e \"s/ChangeMe/IWasChanged/\" <\"$orig_file\" >\"$fortran_file\" &&\n+\tsed -e \"s/ChangeMe/IWasChanged/\" <\"$fortran_file\" >\"$fortran_file\".tmp &&\n+\tmv \"$fortran_file\".tmp \"$fortran_file\" &&\n \tgit add \"$fortran_file\" &&\n \tGIT_AUTHOR_NAME=\"B\" GIT_AUTHOR_EMAIL=\"B@test.git\" \\\n \tgit commit -m \"change fortran file\"\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"417727","messageId":"20210224195129.4004-11-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"[PATCH v3 10/35] blame tests: simplify userdiff driver test","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-24T19:51:04Z","receivedAt":"2021-02-24T19:54:08Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Simplify the test added in 9466e3809d (blame: enable funcname blaming\nwith userdiff driver, 2020-11-01) to use the --author support recently\nadded in 999cfc4f45 (test-lib functions: add --author support to\ntest_commit, 2021-01-12).\n\nWe also did not need the full fortran-external-function content. Let's\ncut it down to just the important parts, and further modify it to\ndemonstrate that the fortran-specific userdiff function is in effect\nby adding \"DO NOT MATCH ...\" and \"AS THE ...\" lines surrounding the\n\"RIGHT\" one. This is to check that we're using the userdiff \"fortran\"\ndriver, as opposed to the default driver.\n\nThe test also left behind a .gitattributes files, let's clean it up\nwith \"test_when_finished\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/annotate-tests.sh | 36 +++++++++++++++---------------------\n 1 file changed, 15 insertions(+), 21 deletions(-)\n\ndiff --git a/t/annotate-tests.sh b/t/annotate-tests.sh\nindex 04a2c58594c..d3b299e75cb 100644\n--- a/t/annotate-tests.sh\n+++ b/t/annotate-tests.sh\n@@ -479,32 +479,26 @@ test_expect_success 'blame -L ^:RE (absolute: end-of-file)' '\n \tcheck_count -f hello.c -L$n -L^:ma.. F 4 G 1 H 1\n '\n \n-test_expect_success 'setup -L :funcname with userdiff driver' '\n-\techo \"fortran-* diff=fortran\" >.gitattributes &&\n-\tfortran_file=fortran-external-function &&\n-\tcat >$fortran_file <<-\\EOF &&\n+test_expect_success 'blame -L :funcname with userdiff driver' '\n+\tcat >file.template <<-\\EOF &&\n+\tDO NOT MATCH THIS LINE\n \tfunction RIGHT(a, b) result(c)\n+\tAS THE DEFAULT DRIVER WOULD\n \n \tinteger, intent(in) :: ChangeMe\n-\tinteger, intent(in) :: b\n-\tinteger, intent(out) :: c\n-\n-\tc = a+b\n-\n-\tend function RIGHT\n \tEOF\n-\tgit add \"$fortran_file\" &&\n-\tGIT_AUTHOR_NAME=\"A\" GIT_AUTHOR_EMAIL=\"A@test.git\" \\\n-\tgit commit -m \"add fortran file\" &&\n-\tsed -e \"s/ChangeMe/IWasChanged/\" <\"$fortran_file\" >\"$fortran_file\".tmp &&\n-\tmv \"$fortran_file\".tmp \"$fortran_file\" &&\n-\tgit add \"$fortran_file\" &&\n-\tGIT_AUTHOR_NAME=\"B\" GIT_AUTHOR_EMAIL=\"B@test.git\" \\\n-\tgit commit -m \"change fortran file\"\n-'\n \n-test_expect_success 'blame -L :funcname with userdiff driver' '\n-\tcheck_count -f fortran-external-function -L:RIGHT A 7 B 1\n+\tfortran_file=file.f03 &&\n+\ttest_when_finished \"rm .gitattributes\" &&\n+\techo \"$fortran_file diff=fortran\" >.gitattributes &&\n+\n+\ttest_commit --author \"A <A@test.git>\" \\\n+\t\t\"add\" \"$fortran_file\" \\\n+\t\t\"$(cat file.template)\" &&\n+\ttest_commit --author \"B <B@test.git>\" \\\n+\t\t\"change\" \"$fortran_file\" \\\n+\t\t\"$(cat file.template | sed -e s/ChangeMe/IWasChanged/)\" &&\n+\tcheck_count -f \"$fortran_file\" -L:RIGHT A 3 B 1\n '\n \n test_expect_success 'setup incremental' '\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"417728","messageId":"20210224195129.4004-13-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"[PATCH v3 12/35] userdiff tests: change setup loop to individual test setup","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-24T19:51:06Z","receivedAt":"2021-02-24T19:54:14Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the recently amended setup loop in \"setup hunk header tests\" to\ninstead set up the test data as we test each individual hunk header\ntest.\n\nThis means we can get rid of the \"|| return 1\" case and the previous\nfor-loop, and won't spend time on setting up all the data only to\ne.g. fail on the 1st test when running under \"-i\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018-diff-funcname.sh | 25 ++++++++++++-------------\n 1 file changed, 12 insertions(+), 13 deletions(-)\n\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex 15dcbe735ca..2365f0e361e 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -72,24 +72,23 @@ test_expect_success 'setup hunk header tests' '\n \n \tcp -R \"$TEST_DIRECTORY\"/t4018 . &&\n \tgit init t4018 &&\n-\tgit -C t4018 add . &&\n+\tgit -C t4018 add .\n+'\n \n-\tfor i in $(git -C t4018 ls-files)\n-\tdo\n-\t\tgrep -v \"^t4018\" \"t4018/$i\" >\"t4018/$i.content\" &&\n-\t\tsed -n -e \"s/^t4018 header: //p\" <\"t4018/$i\" >\"t4018/$i.header\" &&\n-\t\tcp \"t4018/$i.content\" \"$i\" &&\n+# check each individual file\n+for i in $(git -C t4018 ls-files)\n+do\n+\ttest_expect_success \"setup hunk header: $i\" \"\n+\t\tgrep -v '^t4018' \\\"t4018/$i\\\" >\\\"t4018/$i.content\\\" &&\n+\t\tsed -n -e 's/^t4018 header: //p' <\\\"t4018/$i\\\" >\\\"t4018/$i.header\\\" &&\n+\t\tcp \\\"t4018/$i.content\\\" \\\"$i\\\" &&\n \n \t\t# add test file to the index\n-\t\tgit add \"$i\" &&\n+\t\tgit add \\\"$i\\\" &&\n \t\t# place modified file in the worktree\n-\t\tsed -e \"s/ChangeMe/IWasChanged/\" <\"t4018/$i.content\" >\"$i\" || return 1\n-\tdone\n-'\n+\t\tsed -e 's/ChangeMe/IWasChanged/' <\\\"t4018/$i.content\\\" >\\\"$i\\\"\n+\t\"\n \n-# check each individual file\n-for i in $(git ls-files)\n-do\n \ttest_expect_success \"hunk header: $i\" \"\n \t\tgit diff -U1 $i >diff &&\n \t\tsed -n -e 's/^.*@@$//p' -e 's/^.*@@ //p' <diff >ctx &&\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"417729","messageId":"20210224195129.4004-16-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"[PATCH v3 15/35] userdiff tests: add a test with multiple tests in a LANG file","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-24T19:51:09Z","receivedAt":"2021-02-24T19:54:45Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Demonstrate that we can now have tests with multiple tests in a given\nLANG file. This is useful to show rules that don't match, follow-up\ncommits will add some tests like that.\n\nLet's move the \"golang\" test, which I'm going to be modifying soon\nover to to this new convention.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018-diff-funcname.sh        |  2 +-\n t/t4018/README                  |  8 +++++++\n t/t4018/golang                  | 39 +++++++++++++++++++++++++++++++++\n t/t4018/golang-complex-function |  9 --------\n t/t4018/golang-func             |  5 -----\n t/t4018/golang-interface        |  5 -----\n t/t4018/golang-long-func        |  6 -----\n t/t4018/golang-struct           |  5 -----\n 8 files changed, 48 insertions(+), 31 deletions(-)\n create mode 100644 t/t4018/golang\n delete mode 100644 t/t4018/golang-complex-function\n delete mode 100644 t/t4018/golang-func\n delete mode 100644 t/t4018/golang-interface\n delete mode 100644 t/t4018/golang-long-func\n delete mode 100644 t/t4018/golang-struct\n\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex 6fd3dce1364..7fc4291f4be 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -67,7 +67,7 @@ test_expect_success 'last regexp must not be negated' '\n test_expect_success 'setup hunk header tests' '\n \tfor i in $diffpatterns\n \tdo\n-\t\techo \"$i-* diff=$i\"\n+\t\techo \"$i* diff=$i\"\n \tdone > .gitattributes &&\n \n \tcp -R \"$TEST_DIRECTORY\"/t4018 . &&\ndiff --git a/t/t4018/README b/t/t4018/README\nindex 54ae735d5f8..a3220dd6374 100644\n--- a/t/t4018/README\n+++ b/t/t4018/README\n@@ -41,6 +41,9 @@ below) are:\n Create test cases called \"LANG-whatever\" in this directory, where\n \"whatever\" is a brief description of the test.\n \n+You can also stick all the tests into one \"LANG\" file. See \"t4018\n+description\" below.\n+\n Any line starting with \"t4018\" is a control line for the test:\n \n  - The \"t4018 header:\" line above specifies what text must appear in\n@@ -48,6 +51,11 @@ Any line starting with \"t4018\" is a control line for the test:\n    the line for ease of not having to hardcode the line numbers and\n    offsets.\n \n+ - The \"t4018 description:\" line above the test is a convention to add\n+   a human-readable description for the test. Unlike in the case of\n+   the LANG.sh test cases these descriptions don't make it to\n+   \"test_expect_success\", and won't be seen in the test output.\n+\n In many of the test cases the header line includes the token \"RIGHT\",\n this used to be part of the test syntax, but isn't anymore. Now we\n care about the \"t4018 header:\" line, not whatever line contains a\ndiff --git a/t/t4018/golang b/t/t4018/golang\nnew file mode 100644\nindex 00000000000..000e66b1c7b\n--- /dev/null\n+++ b/t/t4018/golang\n@@ -0,0 +1,39 @@\n+t4018 description: complex function\n+t4018 header: func (t *Test) RIGHT(a Type) (Type, error) {\n+type Test struct {\n+\ta Type\n+}\n+\n+func (t *Test) RIGHT(a Type) (Type, error) {\n+\tt.a = a\n+\treturn ChangeMe, nil\n+}\n+\n+t4018 description: func\n+t4018 header: func RIGHT() {\n+func RIGHT() {\n+\ta := 5\n+\tb := ChangeMe\n+}\n+\n+t4018 description: interface\n+t4018 header: type RIGHT interface {\n+type RIGHT interface {\n+\ta() Type\n+\tb() ChangeMe\n+}\n+\n+t4018 description: long func\n+t4018 header: func RIGHT(aVeryVeryVeryLongVariableName AVeryVeryVeryLongType,\n+func RIGHT(aVeryVeryVeryLongVariableName AVeryVeryVeryLongType,\n+\tanotherLongVariableName AnotherLongType) {\n+\ta := 5\n+\tb := ChangeMe\n+}\n+\n+t4018 description: struct\n+t4018 header: type RIGHT struct {\n+type RIGHT struct {\n+\ta Type\n+\tb ChangeMe\n+}\ndiff --git a/t/t4018/golang-complex-function b/t/t4018/golang-complex-function\ndeleted file mode 100644\nindex 0574ba912e6..00000000000\n--- a/t/t4018/golang-complex-function\n+++ /dev/null\n@@ -1,9 +0,0 @@\n-t4018 header: func (t *Test) RIGHT(a Type) (Type, error) {\n-type Test struct {\n-\ta Type\n-}\n-\n-func (t *Test) RIGHT(a Type) (Type, error) {\n-\tt.a = a\n-\treturn ChangeMe, nil\n-}\ndiff --git a/t/t4018/golang-func b/t/t4018/golang-func\ndeleted file mode 100644\nindex 0472cfd9798..00000000000\n--- a/t/t4018/golang-func\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-t4018 header: func RIGHT() {\n-func RIGHT() {\n-\ta := 5\n-\tb := ChangeMe\n-}\ndiff --git a/t/t4018/golang-interface b/t/t4018/golang-interface\ndeleted file mode 100644\nindex 3160a1d4524..00000000000\n--- a/t/t4018/golang-interface\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-t4018 header: type RIGHT interface {\n-type RIGHT interface {\n-\ta() Type\n-\tb() ChangeMe\n-}\ndiff --git a/t/t4018/golang-long-func b/t/t4018/golang-long-func\ndeleted file mode 100644\nindex de83aaafca5..00000000000\n--- a/t/t4018/golang-long-func\n+++ /dev/null\n@@ -1,6 +0,0 @@\n-t4018 header: func RIGHT(aVeryVeryVeryLongVariableName AVeryVeryVeryLongType,\n-func RIGHT(aVeryVeryVeryLongVariableName AVeryVeryVeryLongType,\n-\tanotherLongVariableName AnotherLongType) {\n-\ta := 5\n-\tb := ChangeMe\n-}\ndiff --git a/t/t4018/golang-struct b/t/t4018/golang-struct\ndeleted file mode 100644\nindex fc8022537b2..00000000000\n--- a/t/t4018/golang-struct\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-t4018 header: type RIGHT struct {\n-type RIGHT struct {\n-\ta Type\n-\tb ChangeMe\n-}\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"417731","messageId":"20210224195129.4004-12-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"[PATCH v3 11/35] userdiff tests: match full hunk headers","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-24T19:51:05Z","receivedAt":"2021-02-24T19:54:45Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a regression in the test framework for userdiff added in\nbfa7d01413 (t4018: an infrastructure to test hunk headers,\n2014-03-21).\n\nThe testing infrastructure added in that change went overboard with\nsimplifying the tests, to the point where we lost test coverage.\n\nBefore that we'd been able to test the full context line, or ever\nsince the feature was originally added in f258475a6e (Per-path\nattribute based hunk header selection., 2007-07-06).\n\nAfter bfa7d01413 all we cared about was whether \"RIGHT\" appeared on\nthe line. We thus lost the information about whether or not \"RIGHT\"\nwas extracted from the line for the hunk header, or the line appeared\nin full (or other subset of the line).\n\nLet's bring back coverage for that by extending the custom test syntax\nhere to understand \"t4018\" control lines, we grep those out before\ndoing the add/diff, and understand \"t4018 header: \" lines to declare\nthe full context line we're expecting.\n\nNow when we have failures we'll benefit from the full test_cmp output,\ninstead of just getting a non-zero exit code from \"grep\".\n\nThe \"sed -n -e\" here was originally a single 's/^.*@@\\( \\|$\\)//p'\npattern, but the '\\( \\|$\\)' part had portability issues on OSX and\nAIX.\n\nThe one-off addition of headers here to the test files was done by\ninstrumenting the test itself:\n\n    grep '@@ .* @@.*RIGHT' actual >header.right &&\n    sed 's/^@@.*@@ //' header.right >$i.header.small &&\n    mv \\\"$TEST_DIRECTORY\\\"/t4018/$i tmp &&\n    printf 't4018 header: ' >\\\"$TEST_DIRECTORY\\\"/t4018/$i &&\n    cat $i.header.small >> \\\"$TEST_DIRECTORY\\\"/t4018/$i &&\n    cat tmp >>\\\"$TEST_DIRECTORY\\\"/t4018/$i\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018-diff-funcname.sh                   | 27 +++++++++++++---------\n t/t4018/README                             | 25 ++++++++++++--------\n t/t4018/bash-arithmetic-function           |  1 +\n t/t4018/bash-bashism-style-compact         |  1 +\n t/t4018/bash-bashism-style-function        |  1 +\n t/t4018/bash-bashism-style-whitespace      |  1 +\n t/t4018/bash-conditional-function          |  1 +\n t/t4018/bash-missing-parentheses           |  1 +\n t/t4018/bash-mixed-style-compact           |  1 +\n t/t4018/bash-mixed-style-function          |  1 +\n t/t4018/bash-nested-functions              |  1 +\n t/t4018/bash-other-characters              |  1 +\n t/t4018/bash-posix-style-compact           |  1 +\n t/t4018/bash-posix-style-function          |  1 +\n t/t4018/bash-posix-style-whitespace        |  1 +\n t/t4018/bash-subshell-function             |  1 +\n t/t4018/bash-trailing-comment              |  1 +\n t/t4018/cpp-c++-function                   |  1 +\n t/t4018/cpp-class-constructor              |  1 +\n t/t4018/cpp-class-constructor-mem-init     |  1 +\n t/t4018/cpp-class-definition               |  1 +\n t/t4018/cpp-class-definition-derived       |  1 +\n t/t4018/cpp-class-destructor               |  1 +\n t/t4018/cpp-function-returning-global-type |  1 +\n t/t4018/cpp-function-returning-nested      |  1 +\n t/t4018/cpp-function-returning-pointer     |  1 +\n t/t4018/cpp-function-returning-reference   |  1 +\n t/t4018/cpp-gnu-style-function             |  1 +\n t/t4018/cpp-namespace-definition           |  1 +\n t/t4018/cpp-operator-definition            |  1 +\n t/t4018/cpp-skip-access-specifiers         |  1 +\n t/t4018/cpp-skip-comment-block             |  1 +\n t/t4018/cpp-skip-labels                    |  1 +\n t/t4018/cpp-struct-definition              |  1 +\n t/t4018/cpp-struct-single-line             |  1 +\n t/t4018/cpp-template-function-definition   |  1 +\n t/t4018/cpp-union-definition               |  1 +\n t/t4018/cpp-void-c-function                |  1 +\n t/t4018/css-attribute-value-selector       |  1 +\n t/t4018/css-block-level-@-statements       |  1 +\n t/t4018/css-brace-in-col-1                 |  1 +\n t/t4018/css-class-selector                 |  1 +\n t/t4018/css-colon-eol                      |  1 +\n t/t4018/css-colon-selector                 |  1 +\n t/t4018/css-common                         |  1 +\n t/t4018/css-id-selector                    |  1 +\n t/t4018/css-long-selector-list             |  1 +\n t/t4018/css-prop-sans-indent               |  1 +\n t/t4018/css-root-selector                  |  1 +\n t/t4018/css-short-selector-list            |  1 +\n t/t4018/css-trailing-space                 |  1 +\n t/t4018/custom1-pattern                    |  1 +\n t/t4018/custom2-match-to-end-of-line       |  1 +\n t/t4018/custom3-alternation-in-pattern     |  1 +\n t/t4018/dts-labels                         |  1 +\n t/t4018/dts-node-unitless                  |  1 +\n t/t4018/dts-nodes                          |  1 +\n t/t4018/dts-nodes-boolean-prop             |  1 +\n t/t4018/dts-nodes-comment1                 |  1 +\n t/t4018/dts-nodes-comment2                 |  1 +\n t/t4018/dts-nodes-multiline-prop           |  1 +\n t/t4018/dts-reference                      |  1 +\n t/t4018/dts-root                           |  1 +\n t/t4018/dts-root-comment                   |  1 +\n t/t4018/elixir-do-not-pick-end             |  1 +\n t/t4018/elixir-ex-unit-test                |  1 +\n t/t4018/elixir-function                    |  1 +\n t/t4018/elixir-macro                       |  1 +\n t/t4018/elixir-module                      |  1 +\n t/t4018/elixir-module-func                 |  1 +\n t/t4018/elixir-nested-module               |  1 +\n t/t4018/elixir-private-function            |  1 +\n t/t4018/elixir-protocol                    |  1 +\n t/t4018/elixir-protocol-implementation     |  1 +\n t/t4018/fortran-block-data                 |  1 +\n t/t4018/fortran-comment                    |  1 +\n t/t4018/fortran-comment-keyword            |  1 +\n t/t4018/fortran-comment-legacy             |  1 +\n t/t4018/fortran-comment-legacy-star        |  1 +\n t/t4018/fortran-external-function          |  1 +\n t/t4018/fortran-external-subroutine        |  1 +\n t/t4018/fortran-module                     |  1 +\n t/t4018/fortran-module-procedure           |  1 +\n t/t4018/fortran-program                    |  1 +\n t/t4018/fountain-scene                     |  1 +\n t/t4018/golang-complex-function            |  1 +\n t/t4018/golang-func                        |  1 +\n t/t4018/golang-interface                   |  1 +\n t/t4018/golang-long-func                   |  1 +\n t/t4018/golang-struct                      |  1 +\n t/t4018/java-class-member-function         |  1 +\n t/t4018/markdown-heading-indented          |  1 +\n t/t4018/markdown-heading-non-headings      |  1 +\n t/t4018/matlab-class-definition            |  1 +\n t/t4018/matlab-function                    |  1 +\n t/t4018/matlab-octave-section-1            |  1 +\n t/t4018/matlab-octave-section-2            |  1 +\n t/t4018/matlab-section                     |  1 +\n t/t4018/perl-skip-end-of-heredoc           |  1 +\n t/t4018/perl-skip-forward-decl             |  1 +\n t/t4018/perl-skip-sub-in-pod               |  1 +\n t/t4018/perl-sub-definition                |  1 +\n t/t4018/perl-sub-definition-kr-brace       |  1 +\n t/t4018/php-abstract-class                 |  1 +\n t/t4018/php-abstract-method                |  1 +\n t/t4018/php-class                          |  1 +\n t/t4018/php-final-class                    |  1 +\n t/t4018/php-final-method                   |  1 +\n t/t4018/php-function                       |  1 +\n t/t4018/php-interface                      |  1 +\n t/t4018/php-method                         |  1 +\n t/t4018/php-trait                          |  1 +\n t/t4018/python-async-def                   |  1 +\n t/t4018/python-class                       |  1 +\n t/t4018/python-def                         |  1 +\n t/t4018/python-indented-async-def          |  1 +\n t/t4018/python-indented-class              |  1 +\n t/t4018/python-indented-def                |  1 +\n t/t4018/rust-fn                            |  1 +\n t/t4018/rust-impl                          |  1 +\n t/t4018/rust-macro-rules                   |  1 +\n t/t4018/rust-struct                        |  1 +\n t/t4018/rust-trait                         |  1 +\n 123 files changed, 153 insertions(+), 20 deletions(-)\n\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex 9aec9f8e6de..15dcbe735ca 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -70,16 +70,20 @@ test_expect_success 'setup hunk header tests' '\n \t\techo \"$i-* diff=$i\"\n \tdone > .gitattributes &&\n \n-\t# add all test files to the index\n-\t(\n-\t\tcd \"$TEST_DIRECTORY\"/t4018 &&\n-\t\tgit --git-dir=\"$TRASH_DIRECTORY/.git\" add .\n-\t) &&\n-\n-\t# place modified files in the worktree\n-\tfor i in $(git ls-files)\n+\tcp -R \"$TEST_DIRECTORY\"/t4018 . &&\n+\tgit init t4018 &&\n+\tgit -C t4018 add . &&\n+\n+\tfor i in $(git -C t4018 ls-files)\n \tdo\n-\t\tsed -e \"s/ChangeMe/IWasChanged/\" <\"$TEST_DIRECTORY/t4018/$i\" >\"$i\" || return 1\n+\t\tgrep -v \"^t4018\" \"t4018/$i\" >\"t4018/$i.content\" &&\n+\t\tsed -n -e \"s/^t4018 header: //p\" <\"t4018/$i\" >\"t4018/$i.header\" &&\n+\t\tcp \"t4018/$i.content\" \"$i\" &&\n+\n+\t\t# add test file to the index\n+\t\tgit add \"$i\" &&\n+\t\t# place modified file in the worktree\n+\t\tsed -e \"s/ChangeMe/IWasChanged/\" <\"t4018/$i.content\" >\"$i\" || return 1\n \tdone\n '\n \n@@ -87,8 +91,9 @@ test_expect_success 'setup hunk header tests' '\n for i in $(git ls-files)\n do\n \ttest_expect_success \"hunk header: $i\" \"\n-\t\tgit diff -U1 $i >actual &&\n-\t\tgrep '@@ .* @@.*RIGHT' actual\n+\t\tgit diff -U1 $i >diff &&\n+\t\tsed -n -e 's/^.*@@$//p' -e 's/^.*@@ //p' <diff >ctx &&\n+\t\ttest_cmp t4018/$i.header ctx\n \t\"\n done\n \ndiff --git a/t/t4018/README b/t/t4018/README\nindex 2d25b2b4fc9..0a246bbc10e 100644\n--- a/t/t4018/README\n+++ b/t/t4018/README\n@@ -1,15 +1,22 @@\n-How to write RIGHT test cases\n-=============================\n+t4018 header: description of the test.\n+How to write test cases\n+=======================\n+\n+Create test cases called \"LANG-whatever\" in this directory, where\n+\"LANG\" is e.g. the userdiff driver name, where \"whatever\" is a brief\n+description of the test.\n \n Insert the word \"ChangeMe\" (exactly this form) at a distance of\n at least two lines from the line that must appear in the hunk header.\n \n-The text that must appear in the hunk header must contain the word\n-\"right\", but in all upper-case, like in the title above.\n+Any line starting with \"t4018\" is a control line for the test:\n \n-This text is a bit twisted and out of order, but it is itself a\n-test case for the default hunk header pattern. Know what you are doing\n-if you change it.\n+ - The \"t4018 header:\" line above specifies what text must appear in\n+   the hunk header. We strip away the starting \"@@ [...] @@\" part of\n+   the line for ease of not having to hardcode the line numbers and\n+   offsets.\n \n-BTW, this tests that the head line goes to the hunk header, not the line\n-of equal signs.\n+In many of the test cases the header line includes the token \"RIGHT\",\n+this used to be part of the test syntax, but isn't anymore. Now we\n+care about the \"t4018 header:\" line, not whatever line contains a\n+\"RIGHT\" token.\ndiff --git a/t/t4018/bash-arithmetic-function b/t/t4018/bash-arithmetic-function\nindex c0b276cb50f..f5609bf49eb 100644\n--- a/t/t4018/bash-arithmetic-function\n+++ b/t/t4018/bash-arithmetic-function\n@@ -1,3 +1,4 @@\n+t4018 header: RIGHT()\n RIGHT() ((\n \n     ChangeMe = \"$x\" + \"$y\"\ndiff --git a/t/t4018/bash-bashism-style-compact b/t/t4018/bash-bashism-style-compact\nindex 1ca3126f611..42611aad166 100644\n--- a/t/t4018/bash-bashism-style-compact\n+++ b/t/t4018/bash-bashism-style-compact\n@@ -1,3 +1,4 @@\n+t4018 header: function RIGHT {\n function RIGHT {\n     function InvalidSyntax{\n         :\ndiff --git a/t/t4018/bash-bashism-style-function b/t/t4018/bash-bashism-style-function\nindex f1de4fa831c..823b13a463b 100644\n--- a/t/t4018/bash-bashism-style-function\n+++ b/t/t4018/bash-bashism-style-function\n@@ -1,3 +1,4 @@\n+t4018 header: function RIGHT {\n function RIGHT {\n     :\n     echo 'ChangeMe'\ndiff --git a/t/t4018/bash-bashism-style-whitespace b/t/t4018/bash-bashism-style-whitespace\nindex ade85dd3a50..4f1add4a4e5 100644\n--- a/t/t4018/bash-bashism-style-whitespace\n+++ b/t/t4018/bash-bashism-style-whitespace\n@@ -1,3 +1,4 @@\n+t4018 header: function \tRIGHT \t( \t) \t{\n \t function \tRIGHT \t( \t) \t{\n \n \t    ChangeMe\ndiff --git a/t/t4018/bash-conditional-function b/t/t4018/bash-conditional-function\nindex c5949e829ba..1e71a71a320 100644\n--- a/t/t4018/bash-conditional-function\n+++ b/t/t4018/bash-conditional-function\n@@ -1,3 +1,4 @@\n+t4018 header: RIGHT()\n RIGHT() [[ \\\n \n     \"$a\" > \"$ChangeMe\"\ndiff --git a/t/t4018/bash-missing-parentheses b/t/t4018/bash-missing-parentheses\nindex 8c8a05dd7ab..9233042d91f 100644\n--- a/t/t4018/bash-missing-parentheses\n+++ b/t/t4018/bash-missing-parentheses\n@@ -1,3 +1,4 @@\n+t4018 header: function RIGHT {\n function RIGHT {\n     functionInvalidSyntax {\n         :\ndiff --git a/t/t4018/bash-mixed-style-compact b/t/t4018/bash-mixed-style-compact\nindex d9364cba671..0f93c2be55a 100644\n--- a/t/t4018/bash-mixed-style-compact\n+++ b/t/t4018/bash-mixed-style-compact\n@@ -1,3 +1,4 @@\n+t4018 header: function RIGHT(){\n function RIGHT(){\n     :\n     echo 'ChangeMe'\ndiff --git a/t/t4018/bash-mixed-style-function b/t/t4018/bash-mixed-style-function\nindex 555f9b24667..b3024cdc6dc 100644\n--- a/t/t4018/bash-mixed-style-function\n+++ b/t/t4018/bash-mixed-style-function\n@@ -1,3 +1,4 @@\n+t4018 header: function RIGHT() {\n function RIGHT() {\n \n     ChangeMe\ndiff --git a/t/t4018/bash-nested-functions b/t/t4018/bash-nested-functions\nindex 2c9237ead42..1eee6b9a830 100644\n--- a/t/t4018/bash-nested-functions\n+++ b/t/t4018/bash-nested-functions\n@@ -1,3 +1,4 @@\n+t4018 header: RIGHT()\n outer() {\n     RIGHT() {\n         :\ndiff --git a/t/t4018/bash-other-characters b/t/t4018/bash-other-characters\nindex a3f390d525d..02fa1ec822d 100644\n--- a/t/t4018/bash-other-characters\n+++ b/t/t4018/bash-other-characters\n@@ -1,3 +1,4 @@\n+t4018 header: _RIGHT_0n()\n _RIGHT_0n() {\n \n     ChangeMe\ndiff --git a/t/t4018/bash-posix-style-compact b/t/t4018/bash-posix-style-compact\nindex 045bd2029b7..7ba61dea663 100644\n--- a/t/t4018/bash-posix-style-compact\n+++ b/t/t4018/bash-posix-style-compact\n@@ -1,3 +1,4 @@\n+t4018 header: RIGHT()\n RIGHT(){\n \n     ChangeMe\ndiff --git a/t/t4018/bash-posix-style-function b/t/t4018/bash-posix-style-function\nindex a4d144856e9..8566c3f3838 100644\n--- a/t/t4018/bash-posix-style-function\n+++ b/t/t4018/bash-posix-style-function\n@@ -1,3 +1,4 @@\n+t4018 header: RIGHT()\n RIGHT() {\n \n     ChangeMe\ndiff --git a/t/t4018/bash-posix-style-whitespace b/t/t4018/bash-posix-style-whitespace\nindex 4d984f0aa4d..dcc06da3f67 100644\n--- a/t/t4018/bash-posix-style-whitespace\n+++ b/t/t4018/bash-posix-style-whitespace\n@@ -1,3 +1,4 @@\n+t4018 header: RIGHT \t( \t)\n \t RIGHT \t( \t) \t{\n \n \t    ChangeMe\ndiff --git a/t/t4018/bash-subshell-function b/t/t4018/bash-subshell-function\nindex 80baa09484e..f6b188679a2 100644\n--- a/t/t4018/bash-subshell-function\n+++ b/t/t4018/bash-subshell-function\n@@ -1,3 +1,4 @@\n+t4018 header: RIGHT()\n RIGHT() (\n \n     ChangeMe=2\ndiff --git a/t/t4018/bash-trailing-comment b/t/t4018/bash-trailing-comment\nindex f1edbeda319..16ba9701f3f 100644\n--- a/t/t4018/bash-trailing-comment\n+++ b/t/t4018/bash-trailing-comment\n@@ -1,3 +1,4 @@\n+t4018 header: RIGHT()\n RIGHT() { # Comment\n \n     ChangeMe\ndiff --git a/t/t4018/cpp-c++-function b/t/t4018/cpp-c++-function\nindex 9ee6bbef557..316ad0eb34f 100644\n--- a/t/t4018/cpp-c++-function\n+++ b/t/t4018/cpp-c++-function\n@@ -1,3 +1,4 @@\n+t4018 header: Item RIGHT::DoSomething( Args with_spaces )\n Item RIGHT::DoSomething( Args with_spaces )\n {\n \tChangeMe;\ndiff --git a/t/t4018/cpp-class-constructor b/t/t4018/cpp-class-constructor\nindex ec4f115c250..38552023f00 100644\n--- a/t/t4018/cpp-class-constructor\n+++ b/t/t4018/cpp-class-constructor\n@@ -1,3 +1,4 @@\n+t4018 header: Item::Item(int RIGHT)\n Item::Item(int RIGHT)\n {\n \tChangeMe;\ndiff --git a/t/t4018/cpp-class-constructor-mem-init b/t/t4018/cpp-class-constructor-mem-init\nindex 49a69f37e16..dc48c12ac25 100644\n--- a/t/t4018/cpp-class-constructor-mem-init\n+++ b/t/t4018/cpp-class-constructor-mem-init\n@@ -1,3 +1,4 @@\n+t4018 header: Item::Item(int RIGHT) :\n Item::Item(int RIGHT) :\n \tmember(0)\n {\ndiff --git a/t/t4018/cpp-class-definition b/t/t4018/cpp-class-definition\nindex 11b61da3b75..1f258c8f980 100644\n--- a/t/t4018/cpp-class-definition\n+++ b/t/t4018/cpp-class-definition\n@@ -1,3 +1,4 @@\n+t4018 header: class RIGHT\n class RIGHT\n {\n \tint ChangeMe;\ndiff --git a/t/t4018/cpp-class-definition-derived b/t/t4018/cpp-class-definition-derived\nindex 3b98cd09ab5..1e5ec3b4837 100644\n--- a/t/t4018/cpp-class-definition-derived\n+++ b/t/t4018/cpp-class-definition-derived\n@@ -1,3 +1,4 @@\n+t4018 header: class RIGHT :\n class RIGHT :\n \tpublic Baseclass\n {\ndiff --git a/t/t4018/cpp-class-destructor b/t/t4018/cpp-class-destructor\nindex 54876650965..a06a37169ad 100644\n--- a/t/t4018/cpp-class-destructor\n+++ b/t/t4018/cpp-class-destructor\n@@ -1,3 +1,4 @@\n+t4018 header: RIGHT::~RIGHT()\n RIGHT::~RIGHT()\n {\n \tChangeMe;\ndiff --git a/t/t4018/cpp-function-returning-global-type b/t/t4018/cpp-function-returning-global-type\nindex 1084d5990ef..9bb8f57474f 100644\n--- a/t/t4018/cpp-function-returning-global-type\n+++ b/t/t4018/cpp-function-returning-global-type\n@@ -1,3 +1,4 @@\n+t4018 header: ::Item get::it::RIGHT()\n ::Item get::it::RIGHT()\n {\n \tChangeMe;\ndiff --git a/t/t4018/cpp-function-returning-nested b/t/t4018/cpp-function-returning-nested\nindex d9750aa61a5..3a35a39816a 100644\n--- a/t/t4018/cpp-function-returning-nested\n+++ b/t/t4018/cpp-function-returning-nested\n@@ -1,3 +1,4 @@\n+t4018 header: get::Item get::it::RIGHT()\n get::Item get::it::RIGHT()\n {\n \tChangeMe;\ndiff --git a/t/t4018/cpp-function-returning-pointer b/t/t4018/cpp-function-returning-pointer\nindex ef15657ea8f..9890c5488db 100644\n--- a/t/t4018/cpp-function-returning-pointer\n+++ b/t/t4018/cpp-function-returning-pointer\n@@ -1,3 +1,4 @@\n+t4018 header: const char *get_it_RIGHT(char *ptr)\n const char *get_it_RIGHT(char *ptr)\n {\n \tChangeMe;\ndiff --git a/t/t4018/cpp-function-returning-reference b/t/t4018/cpp-function-returning-reference\nindex 01b051df701..7147ab9a74d 100644\n--- a/t/t4018/cpp-function-returning-reference\n+++ b/t/t4018/cpp-function-returning-reference\n@@ -1,3 +1,4 @@\n+t4018 header: string& get::it::RIGHT(char *ptr)\n string& get::it::RIGHT(char *ptr)\n {\n \tChangeMe;\ndiff --git a/t/t4018/cpp-gnu-style-function b/t/t4018/cpp-gnu-style-function\nindex 08c7c7565ae..91e243f3869 100644\n--- a/t/t4018/cpp-gnu-style-function\n+++ b/t/t4018/cpp-gnu-style-function\n@@ -1,3 +1,4 @@\n+t4018 header: RIGHT(int arg)\n const char *\n RIGHT(int arg)\n {\ndiff --git a/t/t4018/cpp-namespace-definition b/t/t4018/cpp-namespace-definition\nindex 6749980241c..bd32988b3f9 100644\n--- a/t/t4018/cpp-namespace-definition\n+++ b/t/t4018/cpp-namespace-definition\n@@ -1,3 +1,4 @@\n+t4018 header: namespace RIGHT\n namespace RIGHT\n {\n \tChangeMe;\ndiff --git a/t/t4018/cpp-operator-definition b/t/t4018/cpp-operator-definition\nindex 1acd8271592..7e8ba945281 100644\n--- a/t/t4018/cpp-operator-definition\n+++ b/t/t4018/cpp-operator-definition\n@@ -1,3 +1,4 @@\n+t4018 header: Value operator+(Value LEFT, Value RIGHT)\n Value operator+(Value LEFT, Value RIGHT)\n {\n \tChangeMe;\ndiff --git a/t/t4018/cpp-skip-access-specifiers b/t/t4018/cpp-skip-access-specifiers\nindex 4d4a9dbb9db..d61c86d3703 100644\n--- a/t/t4018/cpp-skip-access-specifiers\n+++ b/t/t4018/cpp-skip-access-specifiers\n@@ -1,3 +1,4 @@\n+t4018 header: class RIGHT : public Baseclass\n class RIGHT : public Baseclass\n {\n public:\ndiff --git a/t/t4018/cpp-skip-comment-block b/t/t4018/cpp-skip-comment-block\nindex 3800b9967a5..ea4d9c25911 100644\n--- a/t/t4018/cpp-skip-comment-block\n+++ b/t/t4018/cpp-skip-comment-block\n@@ -1,3 +1,4 @@\n+t4018 header: struct item RIGHT(int i)\n struct item RIGHT(int i)\n // Do not\n // pick up\ndiff --git a/t/t4018/cpp-skip-labels b/t/t4018/cpp-skip-labels\nindex b9c10aba225..cf508647281 100644\n--- a/t/t4018/cpp-skip-labels\n+++ b/t/t4018/cpp-skip-labels\n@@ -1,3 +1,4 @@\n+t4018 header: void RIGHT (void)\n void RIGHT (void)\n {\n repeat:\t\t// C++ comment\ndiff --git a/t/t4018/cpp-struct-definition b/t/t4018/cpp-struct-definition\nindex 521c59fd151..a0f9a16204d 100644\n--- a/t/t4018/cpp-struct-definition\n+++ b/t/t4018/cpp-struct-definition\n@@ -1,3 +1,4 @@\n+t4018 header: struct RIGHT {\n struct RIGHT {\n \tunsigned\n \t/* this bit field looks like a label and should not be picked up */\ndiff --git a/t/t4018/cpp-struct-single-line b/t/t4018/cpp-struct-single-line\nindex a0de5fb800f..0d2378a320a 100644\n--- a/t/t4018/cpp-struct-single-line\n+++ b/t/t4018/cpp-struct-single-line\n@@ -1,3 +1,4 @@\n+t4018 header: struct RIGHT_iterator_tag {};\n void wrong()\n {\n }\ndiff --git a/t/t4018/cpp-template-function-definition b/t/t4018/cpp-template-function-definition\nindex 0cdf5ba5bd4..f1ea1e54f5a 100644\n--- a/t/t4018/cpp-template-function-definition\n+++ b/t/t4018/cpp-template-function-definition\n@@ -1,3 +1,4 @@\n+t4018 header: template<class T> int RIGHT(T arg)\n template<class T> int RIGHT(T arg)\n {\n \tChangeMe;\ndiff --git a/t/t4018/cpp-union-definition b/t/t4018/cpp-union-definition\nindex 7ec94df6973..6c00ab3b430 100644\n--- a/t/t4018/cpp-union-definition\n+++ b/t/t4018/cpp-union-definition\n@@ -1,3 +1,4 @@\n+t4018 header: union RIGHT {\n union RIGHT {\n \tdouble v;\n \tint ChangeMe;\ndiff --git a/t/t4018/cpp-void-c-function b/t/t4018/cpp-void-c-function\nindex 153081e872c..08765096c96 100644\n--- a/t/t4018/cpp-void-c-function\n+++ b/t/t4018/cpp-void-c-function\n@@ -1,3 +1,4 @@\n+t4018 header: void RIGHT (void)\n void RIGHT (void)\n {\n \tChangeMe;\ndiff --git a/t/t4018/css-attribute-value-selector b/t/t4018/css-attribute-value-selector\nindex 918256b20c5..8fe8429eb02 100644\n--- a/t/t4018/css-attribute-value-selector\n+++ b/t/t4018/css-attribute-value-selector\n@@ -1,3 +1,4 @@\n+t4018 header: [class*=\"RIGHT\"] {\n [class*=\"RIGHT\"] {\n     background : #000;\n     border : 10px ChangeMe #C6C6C6;\ndiff --git a/t/t4018/css-block-level-@-statements b/t/t4018/css-block-level-@-statements\nindex d6755f2f3db..0ea1b9eb37c 100644\n--- a/t/t4018/css-block-level-@-statements\n+++ b/t/t4018/css-block-level-@-statements\n@@ -1,3 +1,4 @@\n+t4018 header: @keyframes RIGHT {\n @keyframes RIGHT {\n     from {\n         background : #000;\ndiff --git a/t/t4018/css-brace-in-col-1 b/t/t4018/css-brace-in-col-1\nindex 7831577506a..8bc58ce7098 100644\n--- a/t/t4018/css-brace-in-col-1\n+++ b/t/t4018/css-brace-in-col-1\n@@ -1,3 +1,4 @@\n+t4018 header: RIGHT label.control-label\n RIGHT label.control-label\n {\n     margin-top: 10px!important;\ndiff --git a/t/t4018/css-class-selector b/t/t4018/css-class-selector\nindex f790a0062f4..9b7a2b80e72 100644\n--- a/t/t4018/css-class-selector\n+++ b/t/t4018/css-class-selector\n@@ -1,3 +1,4 @@\n+t4018 header: .RIGHT {\n .RIGHT {\n     background : #000;\n     border : 10px ChangeMe #C6C6C6;\ndiff --git a/t/t4018/css-colon-eol b/t/t4018/css-colon-eol\nindex 5a30553d291..665c9f6af5b 100644\n--- a/t/t4018/css-colon-eol\n+++ b/t/t4018/css-colon-eol\n@@ -1,3 +1,4 @@\n+t4018 header: RIGHT h1 {\n RIGHT h1 {\n color:\n ChangeMe;\ndiff --git a/t/t4018/css-colon-selector b/t/t4018/css-colon-selector\nindex c6d71fb42de..dd0e897155f 100644\n--- a/t/t4018/css-colon-selector\n+++ b/t/t4018/css-colon-selector\n@@ -1,3 +1,4 @@\n+t4018 header: RIGHT a:hover {\n RIGHT a:hover {\n     margin-top:\n     10px!important;\ndiff --git a/t/t4018/css-common b/t/t4018/css-common\nindex 84ed754b33b..8351eac792b 100644\n--- a/t/t4018/css-common\n+++ b/t/t4018/css-common\n@@ -1,3 +1,4 @@\n+t4018 header: RIGHT label.control-label {\n RIGHT label.control-label {\n     margin-top: 10px!important;\n     border : 10px ChangeMe #C6C6C6;\ndiff --git a/t/t4018/css-id-selector b/t/t4018/css-id-selector\nindex 17c5111052d..2a4351a4f0b 100644\n--- a/t/t4018/css-id-selector\n+++ b/t/t4018/css-id-selector\n@@ -1,3 +1,4 @@\n+t4018 header: #RIGHT {\n #RIGHT {\n     background : #000;\n     border : 10px ChangeMe #C6C6C6;\ndiff --git a/t/t4018/css-long-selector-list b/t/t4018/css-long-selector-list\nindex 7ccd25d9ed6..e5fba459290 100644\n--- a/t/t4018/css-long-selector-list\n+++ b/t/t4018/css-long-selector-list\n@@ -1,3 +1,4 @@\n+t4018 header: div ul#RIGHT {\n p.header,\n label.control-label,\n div ul#RIGHT {\ndiff --git a/t/t4018/css-prop-sans-indent b/t/t4018/css-prop-sans-indent\nindex a9e3c86b3c9..8ca742c91f8 100644\n--- a/t/t4018/css-prop-sans-indent\n+++ b/t/t4018/css-prop-sans-indent\n@@ -1,3 +1,4 @@\n+t4018 header: RIGHT, label.control-label {\n RIGHT, label.control-label {\n margin-top: 10px!important;\n padding: 0;\ndiff --git a/t/t4018/css-root-selector b/t/t4018/css-root-selector\nindex 22b958e3694..327d7630649 100644\n--- a/t/t4018/css-root-selector\n+++ b/t/t4018/css-root-selector\n@@ -1,3 +1,4 @@\n+t4018 header: :RIGHT {\n :RIGHT {\n     background : #000;\n     border : 10px ChangeMe #C6C6C6;\ndiff --git a/t/t4018/css-short-selector-list b/t/t4018/css-short-selector-list\nindex 6a0bdee336b..8a8937a5bd8 100644\n--- a/t/t4018/css-short-selector-list\n+++ b/t/t4018/css-short-selector-list\n@@ -1,3 +1,4 @@\n+t4018 header: label.control, div ul#RIGHT {\n label.control, div ul#RIGHT {\n     margin-top: 10px!important;\n     border : 10px ChangeMe #C6C6C6;\ndiff --git a/t/t4018/css-trailing-space b/t/t4018/css-trailing-space\nindex 32b5606c70f..68956baab70 100644\n--- a/t/t4018/css-trailing-space\n+++ b/t/t4018/css-trailing-space\n@@ -1,3 +1,4 @@\n+t4018 header: RIGHT label.control-label {\n RIGHT label.control-label {\n     margin:10px;   \n     padding:10px;\ndiff --git a/t/t4018/custom1-pattern b/t/t4018/custom1-pattern\nindex e8fd59f884d..37a3422384b 100644\n--- a/t/t4018/custom1-pattern\n+++ b/t/t4018/custom1-pattern\n@@ -1,3 +1,4 @@\n+t4018 header: int special, RIGHT;\n public class Beer\n {\n \tint special, RIGHT;\ndiff --git a/t/t4018/custom2-match-to-end-of-line b/t/t4018/custom2-match-to-end-of-line\nindex f88ac318b79..4800bb1c568 100644\n--- a/t/t4018/custom2-match-to-end-of-line\n+++ b/t/t4018/custom2-match-to-end-of-line\n@@ -1,3 +1,4 @@\n+t4018 header: RIGHT_Beer\n public class RIGHT_Beer\n {\n \tint special;\ndiff --git a/t/t4018/custom3-alternation-in-pattern b/t/t4018/custom3-alternation-in-pattern\nindex 5f3769c64fc..bf7df3d9a73 100644\n--- a/t/t4018/custom3-alternation-in-pattern\n+++ b/t/t4018/custom3-alternation-in-pattern\n@@ -1,3 +1,4 @@\n+t4018 header: public static void main(String RIGHT[])\n public class Beer\n {\n \tint special;\ndiff --git a/t/t4018/dts-labels b/t/t4018/dts-labels\nindex b21ef8737bb..6b907dcdedf 100644\n--- a/t/t4018/dts-labels\n+++ b/t/t4018/dts-labels\n@@ -1,3 +1,4 @@\n+t4018 header: label2: RIGHT {\n / {\n \tlabel_1: node1@ff00 {\n \t\tlabel2: RIGHT {\ndiff --git a/t/t4018/dts-node-unitless b/t/t4018/dts-node-unitless\nindex c5287d91416..990730ef6f4 100644\n--- a/t/t4018/dts-node-unitless\n+++ b/t/t4018/dts-node-unitless\n@@ -1,3 +1,4 @@\n+t4018 header: RIGHT {\n / {\n \tlabel_1: node1 {\n \t\tRIGHT {\ndiff --git a/t/t4018/dts-nodes b/t/t4018/dts-nodes\nindex 5a4334bb164..4bcebc85e60 100644\n--- a/t/t4018/dts-nodes\n+++ b/t/t4018/dts-nodes\n@@ -1,3 +1,4 @@\n+t4018 header: RIGHT@deadf00,4000 {\n / {\n \tlabel_1: node1@ff00 {\n \t\tRIGHT@deadf00,4000 {\ndiff --git a/t/t4018/dts-nodes-boolean-prop b/t/t4018/dts-nodes-boolean-prop\nindex afc6b5b404e..404cab6f0e1 100644\n--- a/t/t4018/dts-nodes-boolean-prop\n+++ b/t/t4018/dts-nodes-boolean-prop\n@@ -1,3 +1,4 @@\n+t4018 header: RIGHT@deadf00,4000 {\n / {\n \tlabel_1: node1@ff00 {\n \t\tRIGHT@deadf00,4000 {\ndiff --git a/t/t4018/dts-nodes-comment1 b/t/t4018/dts-nodes-comment1\nindex 559dfce9b30..2e5cea26818 100644\n--- a/t/t4018/dts-nodes-comment1\n+++ b/t/t4018/dts-nodes-comment1\n@@ -1,3 +1,4 @@\n+t4018 header: RIGHT@deadf00,4000 /* &a comment */ {\n / {\n \tlabel_1: node1@ff00 {\n \t\tRIGHT@deadf00,4000 /* &a comment */ {\ndiff --git a/t/t4018/dts-nodes-comment2 b/t/t4018/dts-nodes-comment2\nindex 27e9718b31c..f6cc3177958 100644\n--- a/t/t4018/dts-nodes-comment2\n+++ b/t/t4018/dts-nodes-comment2\n@@ -1,3 +1,4 @@\n+t4018 header: RIGHT@deadf00,4000 { /* a trailing comment */\n / {\n \tlabel_1: node1@ff00 {\n \t\tRIGHT@deadf00,4000 { /* a trailing comment */ \ndiff --git a/t/t4018/dts-nodes-multiline-prop b/t/t4018/dts-nodes-multiline-prop\nindex 072d58b69dc..ac07b404975 100644\n--- a/t/t4018/dts-nodes-multiline-prop\n+++ b/t/t4018/dts-nodes-multiline-prop\n@@ -1,3 +1,4 @@\n+t4018 header: RIGHT@deadf00,4000 {\n / {\n \tlabel_1: node1@ff00 {\n \t\tRIGHT@deadf00,4000 {\ndiff --git a/t/t4018/dts-reference b/t/t4018/dts-reference\nindex 8f0c87d8637..bf141229e93 100644\n--- a/t/t4018/dts-reference\n+++ b/t/t4018/dts-reference\n@@ -1,3 +1,4 @@\n+t4018 header: &RIGHT {\n &label_1 {\n \tTEST = <455>;\n };\ndiff --git a/t/t4018/dts-root b/t/t4018/dts-root\nindex 4353b8220c9..4105b0bb48a 100644\n--- a/t/t4018/dts-root\n+++ b/t/t4018/dts-root\n@@ -1,3 +1,4 @@\n+t4018 header: / { RIGHT /* Technically just supposed to be a slash and brace */\n / { RIGHT /* Technically just supposed to be a slash and brace */\n \t#size-cells = <1>;\n \ndiff --git a/t/t4018/dts-root-comment b/t/t4018/dts-root-comment\nindex 333a625c700..cdf2cee98db 100644\n--- a/t/t4018/dts-root-comment\n+++ b/t/t4018/dts-root-comment\n@@ -1,3 +1,4 @@\n+t4018 header: / { RIGHT /* Technically just supposed to be a slash and brace */\n / { RIGHT /* Technically just supposed to be a slash and brace */\n \t#size-cells = <1>;\n \ndiff --git a/t/t4018/elixir-do-not-pick-end b/t/t4018/elixir-do-not-pick-end\nindex fae08ba7e8c..cf861f81e9a 100644\n--- a/t/t4018/elixir-do-not-pick-end\n+++ b/t/t4018/elixir-do-not-pick-end\n@@ -1,3 +1,4 @@\n+t4018 header: defmodule RIGHT do\n defmodule RIGHT do\n end\n #\ndiff --git a/t/t4018/elixir-ex-unit-test b/t/t4018/elixir-ex-unit-test\nindex 0560a2b6971..469f1a03b71 100644\n--- a/t/t4018/elixir-ex-unit-test\n+++ b/t/t4018/elixir-ex-unit-test\n@@ -1,3 +1,4 @@\n+t4018 header: test \"RIGHT\" do\n defmodule Test do\n   test \"RIGHT\" do\n     assert true == true\ndiff --git a/t/t4018/elixir-function b/t/t4018/elixir-function\nindex d452f495a7e..139a4e1da8e 100644\n--- a/t/t4018/elixir-function\n+++ b/t/t4018/elixir-function\n@@ -1,3 +1,4 @@\n+t4018 header: def function(RIGHT, arg) do\n def function(RIGHT, arg) do\n   # comment\n   # comment\ndiff --git a/t/t4018/elixir-macro b/t/t4018/elixir-macro\nindex 4f925e9ad46..0b0f7d721bb 100644\n--- a/t/t4018/elixir-macro\n+++ b/t/t4018/elixir-macro\n@@ -1,3 +1,4 @@\n+t4018 header: defmacro foo(RIGHT) do\n defmacro foo(RIGHT) do\n   # Code\n   # Code\ndiff --git a/t/t4018/elixir-module b/t/t4018/elixir-module\nindex 91a4e7aa200..a4773f4abae 100644\n--- a/t/t4018/elixir-module\n+++ b/t/t4018/elixir-module\n@@ -1,3 +1,4 @@\n+t4018 header: defmodule RIGHT do\n defmodule RIGHT do\n   @moduledoc \"\"\"\n   Foo bar\ndiff --git a/t/t4018/elixir-module-func b/t/t4018/elixir-module-func\nindex c9910d06751..138493829b6 100644\n--- a/t/t4018/elixir-module-func\n+++ b/t/t4018/elixir-module-func\n@@ -1,3 +1,4 @@\n+t4018 header: def fun(RIGHT) do\n defmodule Foo do\n   def fun(RIGHT) do\n      # Code\ndiff --git a/t/t4018/elixir-nested-module b/t/t4018/elixir-nested-module\nindex 771ebc5c42a..296990dd2db 100644\n--- a/t/t4018/elixir-nested-module\n+++ b/t/t4018/elixir-nested-module\n@@ -1,3 +1,4 @@\n+t4018 header: defmodule MyApp.RIGHT do\n defmodule MyApp.RIGHT do\n   @moduledoc \"\"\"\n   Foo bar\ndiff --git a/t/t4018/elixir-private-function b/t/t4018/elixir-private-function\nindex 1aabe33b7a9..94428e775b5 100644\n--- a/t/t4018/elixir-private-function\n+++ b/t/t4018/elixir-private-function\n@@ -1,3 +1,4 @@\n+t4018 header: defp function(RIGHT, arg) do\n defp function(RIGHT, arg) do\n   # comment\n   # comment\ndiff --git a/t/t4018/elixir-protocol b/t/t4018/elixir-protocol\nindex 7d9173691e3..1f1dc8cb199 100644\n--- a/t/t4018/elixir-protocol\n+++ b/t/t4018/elixir-protocol\n@@ -1,3 +1,4 @@\n+t4018 header: defprotocol RIGHT do\n defprotocol RIGHT do\n   @doc \"\"\"\n   Calculates the size (and not the length!) of a data structure\ndiff --git a/t/t4018/elixir-protocol-implementation b/t/t4018/elixir-protocol-implementation\nindex f9234bbfc48..973226206d7 100644\n--- a/t/t4018/elixir-protocol-implementation\n+++ b/t/t4018/elixir-protocol-implementation\n@@ -1,3 +1,4 @@\n+t4018 header: defimpl RIGHT do\n defimpl RIGHT do\n   # Docs\n   # Docs\ndiff --git a/t/t4018/fortran-block-data b/t/t4018/fortran-block-data\nindex 63d4e21d0ad..eb0a77afade 100644\n--- a/t/t4018/fortran-block-data\n+++ b/t/t4018/fortran-block-data\n@@ -1,3 +1,4 @@\n+t4018 header: BLOCK DATA RIGHT\n        BLOCK DATA RIGHT\n        \n        COMMON /B/ C, ChangeMe\ndiff --git a/t/t4018/fortran-comment b/t/t4018/fortran-comment\nindex 7b10d176588..e5e82331ebc 100644\n--- a/t/t4018/fortran-comment\n+++ b/t/t4018/fortran-comment\n@@ -1,3 +1,4 @@\n+t4018 header: subroutine RIGHT\n       module a\n \n       contains\ndiff --git a/t/t4018/fortran-comment-keyword b/t/t4018/fortran-comment-keyword\nindex e9206a53799..143aff89d73 100644\n--- a/t/t4018/fortran-comment-keyword\n+++ b/t/t4018/fortran-comment-keyword\n@@ -1,3 +1,4 @@\n+t4018 header: subroutine RIGHT (funcA, funcB)\n       module a\n \n       contains\ndiff --git a/t/t4018/fortran-comment-legacy b/t/t4018/fortran-comment-legacy\nindex 53cd062c1e5..edbac0b4148 100644\n--- a/t/t4018/fortran-comment-legacy\n+++ b/t/t4018/fortran-comment-legacy\n@@ -1,3 +1,4 @@\n+t4018 header: subroutine RIGHT\n       module a\n \n       contains\ndiff --git a/t/t4018/fortran-comment-legacy-star b/t/t4018/fortran-comment-legacy-star\nindex 2cbcdc3d8ab..be918f7b030 100644\n--- a/t/t4018/fortran-comment-legacy-star\n+++ b/t/t4018/fortran-comment-legacy-star\n@@ -1,3 +1,4 @@\n+t4018 header: subroutine RIGHT\n       module a\n \n       contains\ndiff --git a/t/t4018/fortran-external-function b/t/t4018/fortran-external-function\nindex 5a2d85d3aa4..ea4d8bcedc4 100644\n--- a/t/t4018/fortran-external-function\n+++ b/t/t4018/fortran-external-function\n@@ -1,3 +1,4 @@\n+t4018 header: function RIGHT(a, b) result(c)\n function RIGHT(a, b) result(c)\n \n integer, intent(in) :: ChangeMe\ndiff --git a/t/t4018/fortran-external-subroutine b/t/t4018/fortran-external-subroutine\nindex 4ce85fea132..9f662dd26b4 100644\n--- a/t/t4018/fortran-external-subroutine\n+++ b/t/t4018/fortran-external-subroutine\n@@ -1,3 +1,4 @@\n+t4018 header: subroutine RIGHT\n subroutine RIGHT\n \n real ChangeMe\ndiff --git a/t/t4018/fortran-module b/t/t4018/fortran-module\nindex c4b737dac3f..1882b3cd338 100644\n--- a/t/t4018/fortran-module\n+++ b/t/t4018/fortran-module\n@@ -1,3 +1,4 @@\n+t4018 header: module RIGHT\n module RIGHT\n \n use ChangeMe\ndiff --git a/t/t4018/fortran-module-procedure b/t/t4018/fortran-module-procedure\nindex 1ce6d854c22..51d368ba9be 100644\n--- a/t/t4018/fortran-module-procedure\n+++ b/t/t4018/fortran-module-procedure\n@@ -1,3 +1,4 @@\n+t4018 header: module RIGHT\n  module RIGHT\n \n    implicit none\ndiff --git a/t/t4018/fortran-program b/t/t4018/fortran-program\nindex 4616895e4b5..1c84e1ff0f8 100644\n--- a/t/t4018/fortran-program\n+++ b/t/t4018/fortran-program\n@@ -1,3 +1,4 @@\n+t4018 header: program RIGHT\n program RIGHT\n \n call ChangeMe\ndiff --git a/t/t4018/fountain-scene b/t/t4018/fountain-scene\nindex 6b3257d6803..2ffcf799087 100644\n--- a/t/t4018/fountain-scene\n+++ b/t/t4018/fountain-scene\n@@ -1,3 +1,4 @@\n+t4018 header: EXT. STREET RIGHT OUTSIDE - DAY\n EXT. STREET RIGHT OUTSIDE - DAY\n \n CHARACTER\ndiff --git a/t/t4018/golang-complex-function b/t/t4018/golang-complex-function\nindex e057dcefed6..0574ba912e6 100644\n--- a/t/t4018/golang-complex-function\n+++ b/t/t4018/golang-complex-function\n@@ -1,3 +1,4 @@\n+t4018 header: func (t *Test) RIGHT(a Type) (Type, error) {\n type Test struct {\n \ta Type\n }\ndiff --git a/t/t4018/golang-func b/t/t4018/golang-func\nindex 8e9c9ac7c3f..0472cfd9798 100644\n--- a/t/t4018/golang-func\n+++ b/t/t4018/golang-func\n@@ -1,3 +1,4 @@\n+t4018 header: func RIGHT() {\n func RIGHT() {\n \ta := 5\n \tb := ChangeMe\ndiff --git a/t/t4018/golang-interface b/t/t4018/golang-interface\nindex 553bedec962..3160a1d4524 100644\n--- a/t/t4018/golang-interface\n+++ b/t/t4018/golang-interface\n@@ -1,3 +1,4 @@\n+t4018 header: type RIGHT interface {\n type RIGHT interface {\n \ta() Type\n \tb() ChangeMe\ndiff --git a/t/t4018/golang-long-func b/t/t4018/golang-long-func\nindex ac3a77b5c41..de83aaafca5 100644\n--- a/t/t4018/golang-long-func\n+++ b/t/t4018/golang-long-func\n@@ -1,3 +1,4 @@\n+t4018 header: func RIGHT(aVeryVeryVeryLongVariableName AVeryVeryVeryLongType,\n func RIGHT(aVeryVeryVeryLongVariableName AVeryVeryVeryLongType,\n \tanotherLongVariableName AnotherLongType) {\n \ta := 5\ndiff --git a/t/t4018/golang-struct b/t/t4018/golang-struct\nindex 5deda77feec..fc8022537b2 100644\n--- a/t/t4018/golang-struct\n+++ b/t/t4018/golang-struct\n@@ -1,3 +1,4 @@\n+t4018 header: type RIGHT struct {\n type RIGHT struct {\n \ta Type\n \tb ChangeMe\ndiff --git a/t/t4018/java-class-member-function b/t/t4018/java-class-member-function\nindex 298bc7a71b2..df77bd71f79 100644\n--- a/t/t4018/java-class-member-function\n+++ b/t/t4018/java-class-member-function\n@@ -1,3 +1,4 @@\n+t4018 header: public static void main(String RIGHT[])\n public class Beer\n {\n \tint special;\ndiff --git a/t/t4018/markdown-heading-indented b/t/t4018/markdown-heading-indented\nindex 1991c2bd456..e68801c7c06 100644\n--- a/t/t4018/markdown-heading-indented\n+++ b/t/t4018/markdown-heading-indented\n@@ -1,3 +1,4 @@\n+t4018 header:    ### RIGHT\n Indented headings are allowed, as long as the indent is no more than 3 spaces.\n \n    ### RIGHT\ndiff --git a/t/t4018/markdown-heading-non-headings b/t/t4018/markdown-heading-non-headings\nindex c479c1a3f1e..00d677ebe95 100644\n--- a/t/t4018/markdown-heading-non-headings\n+++ b/t/t4018/markdown-heading-non-headings\n@@ -1,3 +1,4 @@\n+t4018 header: # RIGHT\n Headings can be right next to other lines of the file:\n # RIGHT\n Indents of four or more spaces make a code block:\ndiff --git a/t/t4018/matlab-class-definition b/t/t4018/matlab-class-definition\nindex 84daedfb4e5..86b48eebe0a 100644\n--- a/t/t4018/matlab-class-definition\n+++ b/t/t4018/matlab-class-definition\n@@ -1,3 +1,4 @@\n+t4018 header: classdef RIGHT\n classdef RIGHT\n     properties\n         ChangeMe\ndiff --git a/t/t4018/matlab-function b/t/t4018/matlab-function\nindex 897a9b13ff4..9a93efffb99 100644\n--- a/t/t4018/matlab-function\n+++ b/t/t4018/matlab-function\n@@ -1,3 +1,4 @@\n+t4018 header: function y = RIGHT()\n function y = RIGHT()\n x = 5;\n y = ChangeMe + x;\ndiff --git a/t/t4018/matlab-octave-section-1 b/t/t4018/matlab-octave-section-1\nindex 3bb6c4670e2..b896cb07ff1 100644\n--- a/t/t4018/matlab-octave-section-1\n+++ b/t/t4018/matlab-octave-section-1\n@@ -1,3 +1,4 @@\n+t4018 header: %%% RIGHT section\n %%% RIGHT section\n # this is octave script\n ChangeMe = 1;\ndiff --git a/t/t4018/matlab-octave-section-2 b/t/t4018/matlab-octave-section-2\nindex ab2980f7f29..4e75942678f 100644\n--- a/t/t4018/matlab-octave-section-2\n+++ b/t/t4018/matlab-octave-section-2\n@@ -1,3 +1,4 @@\n+t4018 header: ## RIGHT section\n ## RIGHT section\n # this is octave script\n ChangeMe = 1;\ndiff --git a/t/t4018/matlab-section b/t/t4018/matlab-section\nindex 5ea59a5de00..6b45ae20009 100644\n--- a/t/t4018/matlab-section\n+++ b/t/t4018/matlab-section\n@@ -1,3 +1,4 @@\n+t4018 header: %% RIGHT section\n %% RIGHT section\n % this is understood by both matlab and octave\n ChangeMe = 1;\ndiff --git a/t/t4018/perl-skip-end-of-heredoc b/t/t4018/perl-skip-end-of-heredoc\nindex c22d39b2567..8f90cca7314 100644\n--- a/t/t4018/perl-skip-end-of-heredoc\n+++ b/t/t4018/perl-skip-end-of-heredoc\n@@ -1,3 +1,4 @@\n+t4018 header: sub RIGHTwithheredocument {\n sub RIGHTwithheredocument {\n \tprint <<\"EOF\"\n decoy here-doc\ndiff --git a/t/t4018/perl-skip-forward-decl b/t/t4018/perl-skip-forward-decl\nindex a98cb8bdad0..ff1f6d14735 100644\n--- a/t/t4018/perl-skip-forward-decl\n+++ b/t/t4018/perl-skip-forward-decl\n@@ -1,3 +1,4 @@\n+t4018 header: package RIGHT;\n package RIGHT;\n \n use strict;\ndiff --git a/t/t4018/perl-skip-sub-in-pod b/t/t4018/perl-skip-sub-in-pod\nindex e39f02462e2..ff1c65b28fc 100644\n--- a/t/t4018/perl-skip-sub-in-pod\n+++ b/t/t4018/perl-skip-sub-in-pod\n@@ -1,3 +1,4 @@\n+t4018 header: =head1 SYNOPSIS_RIGHT\n =head1 NAME\n \n Beer - subroutine to output fragment of a drinking song\ndiff --git a/t/t4018/perl-sub-definition b/t/t4018/perl-sub-definition\nindex a507d1f6452..22e16ad5363 100644\n--- a/t/t4018/perl-sub-definition\n+++ b/t/t4018/perl-sub-definition\n@@ -1,3 +1,4 @@\n+t4018 header: sub RIGHT {\n sub RIGHT {\n \tmy ($n) = @_;\n \tprint \"ChangeMe\";\ndiff --git a/t/t4018/perl-sub-definition-kr-brace b/t/t4018/perl-sub-definition-kr-brace\nindex 330b3df1142..6c94e6a62dd 100644\n--- a/t/t4018/perl-sub-definition-kr-brace\n+++ b/t/t4018/perl-sub-definition-kr-brace\n@@ -1,3 +1,4 @@\n+t4018 header: sub RIGHT\n sub RIGHT\n {\n \tprint \"ChangeMe\\n\";\ndiff --git a/t/t4018/php-abstract-class b/t/t4018/php-abstract-class\nindex 5213e124946..fbc97843c6b 100644\n--- a/t/t4018/php-abstract-class\n+++ b/t/t4018/php-abstract-class\n@@ -1,3 +1,4 @@\n+t4018 header: abstract class RIGHT\n abstract class RIGHT\n {\n     const FOO = 'ChangeMe';\ndiff --git a/t/t4018/php-abstract-method b/t/t4018/php-abstract-method\nindex ce215df75a4..22f5120d3b7 100644\n--- a/t/t4018/php-abstract-method\n+++ b/t/t4018/php-abstract-method\n@@ -1,3 +1,4 @@\n+t4018 header: abstract public function RIGHT(): ?string\n abstract class Klass\n {\n     abstract public function RIGHT(): ?string\ndiff --git a/t/t4018/php-class b/t/t4018/php-class\nindex 7785b6303c7..42456cda768 100644\n--- a/t/t4018/php-class\n+++ b/t/t4018/php-class\n@@ -1,3 +1,4 @@\n+t4018 header: class RIGHT\n class RIGHT\n {\n     const FOO = 'ChangeMe';\ndiff --git a/t/t4018/php-final-class b/t/t4018/php-final-class\nindex 69f57105529..ccf2d28a2d8 100644\n--- a/t/t4018/php-final-class\n+++ b/t/t4018/php-final-class\n@@ -1,3 +1,4 @@\n+t4018 header: final class RIGHT\n final class RIGHT\n {\n     const FOO = 'ChangeMe';\ndiff --git a/t/t4018/php-final-method b/t/t4018/php-final-method\nindex 537fb8ad9ae..55107faef98 100644\n--- a/t/t4018/php-final-method\n+++ b/t/t4018/php-final-method\n@@ -1,3 +1,4 @@\n+t4018 header: final public function RIGHT(): string\n class Klass\n {\n     final public function RIGHT(): string\ndiff --git a/t/t4018/php-function b/t/t4018/php-function\nindex 35717c51c3b..f021285e385 100644\n--- a/t/t4018/php-function\n+++ b/t/t4018/php-function\n@@ -1,3 +1,4 @@\n+t4018 header: function RIGHT()\n function RIGHT()\n {\n     return 'ChangeMe';\ndiff --git a/t/t4018/php-interface b/t/t4018/php-interface\nindex 86b49ad5d9e..ef48244eaa8 100644\n--- a/t/t4018/php-interface\n+++ b/t/t4018/php-interface\n@@ -1,3 +1,4 @@\n+t4018 header: interface RIGHT\n interface RIGHT\n {\n     public function foo($ChangeMe);\ndiff --git a/t/t4018/php-method b/t/t4018/php-method\nindex 03af1a6d9d7..cbc171a99b9 100644\n--- a/t/t4018/php-method\n+++ b/t/t4018/php-method\n@@ -1,3 +1,4 @@\n+t4018 header: public static function RIGHT()\n class Klass\n {\n     public static function RIGHT()\ndiff --git a/t/t4018/php-trait b/t/t4018/php-trait\nindex 65b8c82a616..cba65191d39 100644\n--- a/t/t4018/php-trait\n+++ b/t/t4018/php-trait\n@@ -1,3 +1,4 @@\n+t4018 header: trait RIGHT\n trait RIGHT\n {\n     public function foo($ChangeMe)\ndiff --git a/t/t4018/python-async-def b/t/t4018/python-async-def\nindex 87640e03d21..facfaeef373 100644\n--- a/t/t4018/python-async-def\n+++ b/t/t4018/python-async-def\n@@ -1,3 +1,4 @@\n+t4018 header: async def RIGHT(pi: int = 3.14):\n async def RIGHT(pi: int = 3.14):\n     while True:\n         break\ndiff --git a/t/t4018/python-class b/t/t4018/python-class\nindex ba9e741430f..87153873b4f 100644\n--- a/t/t4018/python-class\n+++ b/t/t4018/python-class\n@@ -1,3 +1,4 @@\n+t4018 header: class RIGHT(int, str):\n class RIGHT(int, str):\n     # comment\n     # another comment\ndiff --git a/t/t4018/python-def b/t/t4018/python-def\nindex e50b31b0ad5..08fb9a6b4ec 100644\n--- a/t/t4018/python-def\n+++ b/t/t4018/python-def\n@@ -1,3 +1,4 @@\n+t4018 header: def RIGHT(pi: int = 3.14):\n def RIGHT(pi: int = 3.14):\n     while True:\n         break\ndiff --git a/t/t4018/python-indented-async-def b/t/t4018/python-indented-async-def\nindex f5d03258af4..f604d08028f 100644\n--- a/t/t4018/python-indented-async-def\n+++ b/t/t4018/python-indented-async-def\n@@ -1,3 +1,4 @@\n+t4018 header: async def RIGHT(self, x: int):\n class Foo:\n     async def RIGHT(self, x: int):\n         return [\ndiff --git a/t/t4018/python-indented-class b/t/t4018/python-indented-class\nindex 19b4f35c4ca..65c07f74f6f 100644\n--- a/t/t4018/python-indented-class\n+++ b/t/t4018/python-indented-class\n@@ -1,3 +1,4 @@\n+t4018 header: class RIGHT:\n if TYPE_CHECKING:\n     class RIGHT:\n         # comment\ndiff --git a/t/t4018/python-indented-def b/t/t4018/python-indented-def\nindex 208fbadd2be..be87764e31b 100644\n--- a/t/t4018/python-indented-def\n+++ b/t/t4018/python-indented-def\n@@ -1,3 +1,4 @@\n+t4018 header: def RIGHT(self, x: int):\n class Foo:\n     def RIGHT(self, x: int):\n         return [\ndiff --git a/t/t4018/rust-fn b/t/t4018/rust-fn\nindex cbe02155f11..939b131ea80 100644\n--- a/t/t4018/rust-fn\n+++ b/t/t4018/rust-fn\n@@ -1,3 +1,4 @@\n+t4018 header: pub(self) fn RIGHT<T>(x: &[T]) where T: Debug {\n pub(self) fn RIGHT<T>(x: &[T]) where T: Debug {\n     let _ = x;\n     // a comment\ndiff --git a/t/t4018/rust-impl b/t/t4018/rust-impl\nindex 09df3cd93b2..1f798a43d38 100644\n--- a/t/t4018/rust-impl\n+++ b/t/t4018/rust-impl\n@@ -1,3 +1,4 @@\n+t4018 header: impl<'a, T: AsRef<[u8]>>  std::RIGHT for Git<'a> {\n impl<'a, T: AsRef<[u8]>>  std::RIGHT for Git<'a> {\n \n     pub fn ChangeMe(&self) -> () {\ndiff --git a/t/t4018/rust-macro-rules b/t/t4018/rust-macro-rules\nindex ec610c5b62b..3990daf0e90 100644\n--- a/t/t4018/rust-macro-rules\n+++ b/t/t4018/rust-macro-rules\n@@ -1,3 +1,4 @@\n+t4018 header: macro_rules! RIGHT {\n macro_rules! RIGHT {\n     () => {\n         // a comment\ndiff --git a/t/t4018/rust-struct b/t/t4018/rust-struct\nindex 76aff1c0d8e..8c901a437cb 100644\n--- a/t/t4018/rust-struct\n+++ b/t/t4018/rust-struct\n@@ -1,3 +1,4 @@\n+t4018 header: pub(super) struct RIGHT<'a> {\n #[derive(Debug)]\n pub(super) struct RIGHT<'a> {\n     name: &'a str,\ndiff --git a/t/t4018/rust-trait b/t/t4018/rust-trait\nindex ea397f09ed1..4cc9714e8f0 100644\n--- a/t/t4018/rust-trait\n+++ b/t/t4018/rust-trait\n@@ -1,3 +1,4 @@\n+t4018 header: unsafe trait RIGHT<T> {\n unsafe trait RIGHT<T> {\n     fn len(&self) -> u32;\n     fn ChangeMe(&self, n: u32) -> T;\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"417730","messageId":"20210224195129.4004-17-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"[PATCH v3 16/35] userdiff tests: do config teardown in test_diff_funcname()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-24T19:51:10Z","receivedAt":"2021-02-24T19:54:46Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Do a teardown of any custom \"diff.<what>.x?funcname\" config after a\ntest_diff_funcname() test runs. Nothing currently uses this, but a\nfollow-up commit will start setting custom config before certain\ntests. Centralizing this teardown makes the tests simpler.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018-diff-funcname.sh | 7 +++++++\n 1 file changed, 7 insertions(+)\n\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex 7fc4291f4be..496313fc900 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -123,6 +123,13 @@ test_diff_funcname () {\n \t\tgit diff -U1 \"$what\" >diff &&\n \t\tlast_diff_context_line diff >actual &&\n \t\ttest_cmp expected actual\n+\t' &&\n+\n+\ttest_expect_success \"teardown: $desc\" '\n+\t\t# In case any custom config was set immediately before\n+\t\t# the test itself in the test file\n+\t\ttest_unconfig \"diff.$what.funcname\" &&\n+\t\ttest_unconfig \"diff.$what.xfuncname\"\n \t'\n }\n \n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"417732","messageId":"20210224195129.4004-20-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"[PATCH v3 19/35] userdiff tests: do not do compile tests on \"custom\" pattern","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-24T19:51:13Z","receivedAt":"2021-02-24T19:54:52Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Since f1b75fbaf1 (t4018: convert custom pattern test to the new\ninfrastructure, 2014-03-21) we have been doing the basic sanity check\nof whether patterns in userdiff.c compile on the \"custom\" patterns.\n\nThat we were doing this was an emergent effect of that change and an\nearlier refactoring in bfa7d01413 (t4018: an infrastructure to test\nhunk headers, 2014-03-21).\n\nThis was never intended by the test added in\ne3bf5e43fd (t4018-diff-funcname: test syntax of builtin xfuncname\npatterns, 2008-09-22), nor is there any point in doing this. We'll\nerror out in the custom.sh test itself if those patterns don't\ncompile.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018-diff-funcname.sh | 12 ++++++------\n 1 file changed, 6 insertions(+), 6 deletions(-)\n\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex 71a7b474cd4..b80546b4d7f 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -20,12 +20,7 @@ test_expect_success 'setup' '\n \techo B >B.java\n '\n \n-diffpatterns=\"\n-\t$(cat builtin-drivers)\n-\tcustom\n-\"\n-\n-for p in $diffpatterns\n+for p in $(cat builtin-drivers)\n do\n \ttest_expect_success \"builtin $p pattern compiles\" '\n \t\techo \"*.java diff=$p\" >.gitattributes &&\n@@ -50,6 +45,11 @@ test_expect_success 'last regexp must not be negated' '\n \ttest_i18ngrep \": Last expression must not be negated:\" msg\n '\n \n+diffpatterns=\"\n+\t$(cat builtin-drivers)\n+\tcustom\n+\"\n+\n test_expect_success 'setup hunk header tests' '\n \tfor i in $diffpatterns\n \tdo\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"417733","messageId":"20210224195129.4004-22-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"[PATCH v3 21/35] userdiff tests + docs: document & test \"diff.<driver>.x?funcname\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-24T19:51:15Z","receivedAt":"2021-02-24T19:55:01Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add the missing documentation for \"diff.<driver>.funcname\" and test\nfor how it and \"diff.<driver>.xfuncname\" interact.\n\nBetween the introduction of the \"diff.<driver>.xfuncname\" form in\n45d9414fa5 (diff.*.xfuncname which uses \"extended\" regex's for hunk\nheader selection, 2008-09-18) and when this documentation was written\nin 90b94c26f7 (Documentation: Add diff.<driver>.* to config,\n2011-04-07) we forgot to document the existence of\n\"diff.<driver>.funcname\".\n\nLet's make a mention of it here, we could also partially revert the\nformer commit and discuss the more verbose form in gitattributes(5),\nbut let's stop short of that. It makes sense to guide users towards\nERE over BRE whenever possible.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/config/diff.txt | 11 +++++++++++\n t/t4018/custom.sh             | 34 ++++++++++++++++++++++++++++++++++\n 2 files changed, 45 insertions(+)\n\ndiff --git a/Documentation/config/diff.txt b/Documentation/config/diff.txt\nindex 2d3331f55c2..6f39ef1da93 100644\n--- a/Documentation/config/diff.txt\n+++ b/Documentation/config/diff.txt\n@@ -153,10 +153,21 @@ diff.<driver>.command::\n \tThe custom diff driver command.  See linkgit:gitattributes[5]\n \tfor details.\n \n+diff.<driver>.funcname::\n diff.<driver>.xfuncname::\n \tThe regular expression that the diff driver should use to\n \trecognize the hunk header.  A built-in pattern may also be used.\n \tSee linkgit:gitattributes[5] for details.\n++\n+When provided as `diff.<driver>.funcname` the regular expression is\n+interpreted as a basic regular expression. With\n+`diff.<driver>.xfuncname` it's interpreted as an extended regular\n+expression.\n++\n+The `*.funcname` and `*.xfuncname` variables behave as if though they\n+were one configuration variable for the purposes of what value\n+eventually gets used. Setting `*.funcname` will override an earlier\n+`*.xfuncname` and vice-versa.\n \n diff.<driver>.binary::\n \tSet this option to true to make the diff driver treat files as\ndiff --git a/t/t4018/custom.sh b/t/t4018/custom.sh\nindex b208a771d28..72d38dad686 100755\n--- a/t/t4018/custom.sh\n+++ b/t/t4018/custom.sh\n@@ -77,3 +77,37 @@ public class Beer\n \t}\n }\n EOF_TEST\n+\n+test_expect_success 'custom: setup config precedence' '\n+\tgit config diff.custom.funcname \"foo\" &&\n+\tgit config diff.custom.xfuncname \"bar\"\n+'\n+\n+test_diff_funcname 'custom: config precedence' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+bar\n+EOF_HUNK\n+foo\n+bar\n+\n+ChangeMe\n+\n+baz\n+EOF_TEST\n+\n+test_expect_success 'custom: setup config precedence' '\n+\tgit config diff.custom.xfuncname \"bar\" &&\n+\tgit config diff.custom.funcname \"foo\"\n+'\n+\n+test_diff_funcname 'custom: config precedence' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+foo\n+EOF_HUNK\n+foo\n+bar\n+\n+ChangeMe\n+\n+baz\n+EOF_TEST\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"417734","messageId":"20210224195129.4004-26-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"[PATCH v3 25/35] gitattributes doc: document multi-line userdiff patterns","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-24T19:51:19Z","receivedAt":"2021-02-24T19:55:13Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Document the multi-line userdiff patterns and how their matching and\nthe negation syntax works.\n\nThese patterns have been supported since f258475a6e (Per-path\nattribute based hunk header selection., 2007-07-06), and have had\ntheir current semantics ever since 3d8dccd74a (diff: fix \"multiple\nregexp\" semantics to find hunk header comment, 2008-09-20).\n\nBut we had no documentation for them, let's fix that, and also add\ntests showing how some of the things being discussed here work.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/gitattributes.txt | 38 ++++++++++++++++--\n t/t4018/custom.sh               | 70 +++++++++++++++++++++++++++++++++\n t/t4018/perl.sh                 | 16 ++++++++\n 3 files changed, 120 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\nindex 62c1147ba97..8082ff1ee73 100644\n--- a/Documentation/gitattributes.txt\n+++ b/Documentation/gitattributes.txt\n@@ -794,12 +794,42 @@ backslashes; the pattern above picks a line that begins with a\n backslash, and zero or more occurrences of `sub` followed by\n `section` followed by open brace, to the end of line.\n \n-There are built-in patterns shipped as part of git itself. A more\n-advanced version of the `tex` pattern discussed above is one of them.\n+Multiple patterns can be supplied by listing them one per line\n+separated by `\\n`. They will be matched one line at a time, e.g.:\n+\n+------------------------\n+[diff \"perl\"]\n+\txfuncname = \"!^=head\\n^[^ ]+.*\"\n+------------------------\n+\n+Patterns in a list of multiple patterns that begin with \"!\" are\n+negated. A matching negated pattern will cause the matched line to be\n+skipped. Use it to skip a later pattern that would otherwise match. It\n+is an error if one or more negated patterns aren't followed by a\n+non-negated pattern.\n+\n+To match a literal \"!\" at the start of a line, use some other regex\n+construct that will match a literal \"!\" without \"!\" being the first\n+character on that line, such as \"[!]\".\n+\n+If the last pattern in a list of multiple patterns ends with \"\\n\" it\n+will be interpreted as an empty pattern, and will match the first\n+empty line. It's almost always a logic error to provide a list of\n+multiple patterns ending with \"\\n\", but it's permitted in case you\n+genuinely want to match an empty line.\n+\n+If the pattern contains a `$1` capture it will be used instead of the\n+entire matching line (`$0`) to display the hunk header. This can be\n+used e.g. to strip whitespace from the beginning of the line, or to\n+only display the function name as part of a longer function\n+definition.\n+\n+There are built-in patterns shipped as part of git itself, see the\n+full listing below.\n \n For built-in patterns, you do not need `diff.<lang>.xfuncname` in your\n-configuration file as discussed above, but if present, it will\n-override a built-in pattern.\n+configuration file. If present, it will override a built-in pattern,\n+as shown in the `diff.perl.xfuncname` example above.\n \n Nevertheless, you need to enable built-in patterns via .gitattributes`\n for the pattern to take effect.\ndiff --git a/t/t4018/custom.sh b/t/t4018/custom.sh\nindex 72d38dad686..127524afda3 100755\n--- a/t/t4018/custom.sh\n+++ b/t/t4018/custom.sh\n@@ -111,3 +111,73 @@ ChangeMe\n \n baz\n EOF_TEST\n+\n+test_expect_success 'custom: setup negation syntax, ! is magic' '\n+\tgit config diff.custom.xfuncname \"!negation\n+line\"\n+'\n+\n+test_diff_funcname 'custom: negation syntax, ! is magic' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+line\n+EOF_HUNK\n+line\n+!negation\n+\n+ChangeMe\n+\n+baz\n+EOF_TEST\n+\n+test_expect_success 'custom: setup negation syntax, use [!] to override ! magic' '\n+\tgit config diff.custom.xfuncname \"[!]negation\n+line\"\n+'\n+\n+test_diff_funcname 'custom: negation syntax, use [!] to override ! magic' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+!negation\n+EOF_HUNK\n+line\n+!negation\n+\n+ChangeMe\n+\n+baz\n+EOF_TEST\n+\n+test_expect_success 'custom: setup captures in multiple patterns' '\n+\tgit config diff.custom.xfuncname \"!^=head\n+^format ([^ ]+)\n+^sub ([^;]+)\"\n+'\n+\n+test_diff_funcname 'custom: captures in multiple patterns' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+foo\n+EOF_HUNK\n+sub foo;\n+\n+=head1\n+\n+ChangeMe\n+\n+EOF_TEST\n+\n+test_expect_success 'custom: multiple patterns ending with \\n' '\n+\tgit config diff.custom.xfuncname \"!^=head\n+^sub ([^;]+)\n+\"\n+'\n+\n+test_diff_funcname 'custom: multiple patterns ending with \\n' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+\n+EOF_HUNK\n+sub foo;\n+\n+=head1\n+\n+ChangeMe\n+\n+EOF_TEST\ndiff --git a/t/t4018/perl.sh b/t/t4018/perl.sh\nindex b53b759353b..ba11241750b 100755\n--- a/t/t4018/perl.sh\n+++ b/t/t4018/perl.sh\n@@ -76,3 +76,19 @@ sub asub\n \tprint \"ChangeMe\\n\";\n }\n EOF_TEST\n+\n+\n+test_expect_success 'custom: setup config overrides built-in patterns' '\n+\tgit config diff.perl.xfuncname \"!^=head\n+^[^ ]+.*\"\n+'\n+\n+test_diff_funcname 'custom: config overrides built-in patterns' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+sub foo;\n+EOF_HUNK\n+sub foo;\n+=head1\n+\n+ChangeMe\n+EOF_TEST\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"417735","messageId":"20210224195129.4004-24-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"[PATCH v3 23/35] userdiff tests: move perl tests to perl.sh","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-24T19:51:17Z","receivedAt":"2021-02-24T19:55:25Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Move the perl tests to perl.sh, a follow-up change will piggy-back on\nthese tests for updating the userdiff documentation. This will require\nthe new test *.sh test framework.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018/perl-skip-end-of-heredoc     |  9 ----\n t/t4018/perl-skip-forward-decl       | 11 ----\n t/t4018/perl-skip-sub-in-pod         | 19 -------\n t/t4018/perl-sub-definition          |  5 --\n t/t4018/perl-sub-definition-kr-brace |  5 --\n t/t4018/perl.sh                      | 78 ++++++++++++++++++++++++++++\n 6 files changed, 78 insertions(+), 49 deletions(-)\n delete mode 100644 t/t4018/perl-skip-end-of-heredoc\n delete mode 100644 t/t4018/perl-skip-forward-decl\n delete mode 100644 t/t4018/perl-skip-sub-in-pod\n delete mode 100644 t/t4018/perl-sub-definition\n delete mode 100644 t/t4018/perl-sub-definition-kr-brace\n create mode 100755 t/t4018/perl.sh\n\ndiff --git a/t/t4018/perl-skip-end-of-heredoc b/t/t4018/perl-skip-end-of-heredoc\ndeleted file mode 100644\nindex 8f90cca7314..00000000000\n--- a/t/t4018/perl-skip-end-of-heredoc\n+++ /dev/null\n@@ -1,9 +0,0 @@\n-t4018 header: sub RIGHTwithheredocument {\n-sub RIGHTwithheredocument {\n-\tprint <<\"EOF\"\n-decoy here-doc\n-EOF\n-\t# some lines of context\n-\t# to pad it out\n-\tprint \"ChangeMe\\n\";\n-}\ndiff --git a/t/t4018/perl-skip-forward-decl b/t/t4018/perl-skip-forward-decl\ndeleted file mode 100644\nindex ff1f6d14735..00000000000\n--- a/t/t4018/perl-skip-forward-decl\n+++ /dev/null\n@@ -1,11 +0,0 @@\n-t4018 header: package RIGHT;\n-package RIGHT;\n-\n-use strict;\n-use warnings;\n-use parent qw(Exporter);\n-our @EXPORT_OK = qw(round finalround);\n-\n-sub other; # forward declaration\n-\n-# ChangeMe\ndiff --git a/t/t4018/perl-skip-sub-in-pod b/t/t4018/perl-skip-sub-in-pod\ndeleted file mode 100644\nindex ff1c65b28fc..00000000000\n--- a/t/t4018/perl-skip-sub-in-pod\n+++ /dev/null\n@@ -1,19 +0,0 @@\n-t4018 header: =head1 SYNOPSIS_RIGHT\n-=head1 NAME\n-\n-Beer - subroutine to output fragment of a drinking song\n-\n-=head1 SYNOPSIS_RIGHT\n-\n-\tuse Beer qw(round finalround);\n-\n-\tsub song {\n-\t\tfor (my $i = 99; $i > 0; $i--) {\n-\t\t\tround $i;\n-\t\t}\n-\t\tfinalround;\n-\t}\n-\n-\tChangeMe;\n-\n-=cut\ndiff --git a/t/t4018/perl-sub-definition b/t/t4018/perl-sub-definition\ndeleted file mode 100644\nindex 22e16ad5363..00000000000\n--- a/t/t4018/perl-sub-definition\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-t4018 header: sub RIGHT {\n-sub RIGHT {\n-\tmy ($n) = @_;\n-\tprint \"ChangeMe\";\n-}\ndiff --git a/t/t4018/perl-sub-definition-kr-brace b/t/t4018/perl-sub-definition-kr-brace\ndeleted file mode 100644\nindex 6c94e6a62dd..00000000000\n--- a/t/t4018/perl-sub-definition-kr-brace\n+++ /dev/null\n@@ -1,5 +0,0 @@\n-t4018 header: sub RIGHT\n-sub RIGHT\n-{\n-\tprint \"ChangeMe\\n\";\n-}\ndiff --git a/t/t4018/perl.sh b/t/t4018/perl.sh\nnew file mode 100755\nindex 00000000000..ac8fff7417a\n--- /dev/null\n+++ b/t/t4018/perl.sh\n@@ -0,0 +1,78 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'perl: skip end of heredoc' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+sub RIGHTwithheredocument {\n+EOF_HUNK\n+sub RIGHTwithheredocument {\n+\tprint <<\"EOF\"\n+decoy here-doc\n+EOF\n+\t# some lines of context\n+\t# to pad it out\n+\tprint \"ChangeMe\\n\";\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'perl: skip forward decl' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+package RIGHT;\n+EOF_HUNK\n+package RIGHT;\n+\n+use strict;\n+use warnings;\n+use parent qw(Exporter);\n+our @EXPORT_OK = qw(round finalround);\n+\n+sub other; # forward declaration\n+\n+# ChangeMe\n+EOF_TEST\n+\n+test_diff_funcname 'perl: skip sub in pod' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+=head1 SYNOPSIS_RIGHT\n+EOF_HUNK\n+=head1 NAME\n+\n+Beer - subroutine to output fragment of a drinking song\n+\n+=head1 SYNOPSIS_RIGHT\n+\n+\tuse Beer qw(round finalround);\n+\n+\tsub song {\n+\t\tfor (my $i = 99; $i > 0; $i--) {\n+\t\t\tround $i;\n+\t\t}\n+\t\tfinalround;\n+\t}\n+\n+\tChangeMe;\n+\n+=cut\n+EOF_TEST\n+\n+test_diff_funcname 'perl: sub definition' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+sub RIGHT {\n+EOF_HUNK\n+sub RIGHT {\n+\tmy ($n) = @_;\n+\tprint \"ChangeMe\";\n+}\n+EOF_TEST\n+\n+test_diff_funcname 'perl: sub definition kr brace' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+sub RIGHT\n+EOF_HUNK\n+sub RIGHT\n+{\n+\tprint \"ChangeMe\\n\";\n+}\n+EOF_TEST\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"417736","messageId":"20210224195129.4004-28-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"[PATCH v3 27/35] userdiff tests: remove \"funcname\" from custom3 test","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-24T19:51:21Z","receivedAt":"2021-02-24T19:55:45Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"We can only have one \"funcname\" or \"xfuncname\", any later definition\noverrides the earlier one, so this configuration wasn't doing\nanything.\n\nWhen this test was originally added in 3632cfc248 (Use compatibility\nregex library for OSX/Darwin, 2008-09-07) we had no such definition of\ntwo patters for this test. Back then this was setting the\n\"diff.java.funcname\" configuration variable.\n\nThe stage for that second pattern being set got set later. In\n45d9414fa5 (diff.*.xfuncname which uses \"extended\" regex's for hunk\nheader selection, 2008-09-18) the pattern got converted from\n\"funcname\" to \"xfuncname\".\n\nSoon after in b19d288b4d (t4018-diff-funcname: demonstrate end of line\nfuncname matching flaw, 2008-10-15) another test immediately preceding\nthis one got added, using \"diff.java.funcname\" for its configuration.\n\nThen f792a0b88e (t4018 (funcname patterns): make configuration easier\nto track, 2011-05-21) came along and codified this whole thing when\nconverting the two tests from \"git config\" to \"test_config\".\n\nSince this was never the intent of the test let's just remove this,\nthe rationale in f792a0b88e for having some test for the clobbering\nbehavior makes sense, but I'll do that in another follow-up test, not\nas a hard to read side-effect of this one.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018/custom.sh | 1 -\n 1 file changed, 1 deletion(-)\n\ndiff --git a/t/t4018/custom.sh b/t/t4018/custom.sh\nindex 97f310c02fb..58187c2293b 100755\n--- a/t/t4018/custom.sh\n+++ b/t/t4018/custom.sh\n@@ -52,7 +52,6 @@ public class Beer\n EOF_TEST\n \n test_expect_success 'custom: setup alternation in pattern' '\n-\tgit config diff.custom.funcname \"Beer$\" &&\n \tgit config diff.custom.xfuncname \"^[ \t]*((public|static).*)$\"\n '\n \n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"417737","messageId":"20210224195129.4004-14-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"[PATCH v3 13/35] userdiff tests: factor out test_diff_funcname() logic","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-24T19:51:07Z","receivedAt":"2021-02-24T19:55:56Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Factor out logic in test_diff_funcname() into two helper functions,\nthese will be useful in a follow-up commit where we'll do this munging\nin more than one place.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018-diff-funcname.sh | 16 +++++++++++++---\n 1 file changed, 13 insertions(+), 3 deletions(-)\n\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex 2365f0e361e..8a8a7a99c88 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -75,6 +75,17 @@ test_expect_success 'setup hunk header tests' '\n \tgit -C t4018 add .\n '\n \n+do_change_me () {\n+\tfile=$1\n+\tsed -e \"s/ChangeMe/IWasChanged/\" <\"$file\" >tmp &&\n+\tmv tmp \"$file\"\n+}\n+\n+last_diff_context_line () {\n+\tfile=$1\n+\tsed -n -e \"s/^.*@@$//p\" -e \"s/^.*@@ //p\" <$file\n+}\n+\n # check each individual file\n for i in $(git -C t4018 ls-files)\n do\n@@ -85,13 +96,12 @@ do\n \n \t\t# add test file to the index\n \t\tgit add \\\"$i\\\" &&\n-\t\t# place modified file in the worktree\n-\t\tsed -e 's/ChangeMe/IWasChanged/' <\\\"t4018/$i.content\\\" >\\\"$i\\\"\n+\t\tdo_change_me \\\"$i\\\"\n \t\"\n \n \ttest_expect_success \"hunk header: $i\" \"\n \t\tgit diff -U1 $i >diff &&\n-\t\tsed -n -e 's/^.*@@$//p' -e 's/^.*@@ //p' <diff >ctx &&\n+\t\tlast_diff_context_line diff >ctx &&\n \t\ttest_cmp t4018/$i.header ctx\n \t\"\n done\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"417738","messageId":"20210224195129.4004-15-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"[PATCH v3 14/35] userdiff tests: add alternative hunk header test infrastructure","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-24T19:51:08Z","receivedAt":"2021-02-24T19:56:04Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add an alternative to the hunk header test infrastructure introduced\nin bfa7d01413 (t4018: an infrastructure to test hunk headers,\n2014-03-21). See c228a5c077 (Merge branch 'js/userdiff-cc',\n2014-03-31) for the whole series that commit was part of.\n\nAs noted in an earlier commit that change introduced the regression of\nnot testing for the full hunk line, but just whether \"RIGHT\" appeared\non it.\n\nA preceding commit fixed that specific issue, but we are still left\nwith the inflexibility of not being able to do any testing except that\nwhich fits inside the narrow confines of this custom test syntax,\ne.g. being unable to setup config variables before specific tests.\n\nSo introduce a new \"test_diff_funcname()\" function which is expected\nto be used by tests in t4018/*.sh, and move the \"custom\" tests over to\nthat. As will be seen in follow-up changes we'll make use of this new\nfunction infrastructure in the \"custom\" tests.\n\nThere's no reason for not moving all of the tests over to this new\ninfrastructure as far as test coverage goes, but Junio and Johannes\nexpressed a desire to keep the existing test mode [1], so I'm only\nmoving a narrow set of tests over to the new mode.\n\n1. https://lore.kernel.org/git/xmqqsg5vrhha.fsf@gitster.c.googlers.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018-diff-funcname.sh                      | 38 +++++++++++++++-\n t/t4018/README                                | 44 ++++++++++++++++---\n t/t4018/{custom1-pattern => custom1.sh}       | 11 ++++-\n t/t4018/custom2-match-to-end-of-line          |  9 ----\n t/t4018/custom2.sh                            | 18 ++++++++\n ...tom3-alternation-in-pattern => custom3.sh} | 11 ++++-\n 6 files changed, 113 insertions(+), 18 deletions(-)\n rename t/t4018/{custom1-pattern => custom1.sh} (71%)\n mode change 100644 => 100755\n delete mode 100644 t/t4018/custom2-match-to-end-of-line\n create mode 100755 t/t4018/custom2.sh\n rename t/t4018/{custom3-alternation-in-pattern => custom3.sh} (66%)\n mode change 100644 => 100755\n\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex 8a8a7a99c88..6fd3dce1364 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -87,7 +87,7 @@ last_diff_context_line () {\n }\n \n # check each individual file\n-for i in $(git -C t4018 ls-files)\n+for i in $(git -C t4018 ls-files -- ':!*.sh')\n do\n \ttest_expect_success \"setup hunk header: $i\" \"\n \t\tgrep -v '^t4018' \\\"t4018/$i\\\" >\\\"t4018/$i.content\\\" &&\n@@ -106,4 +106,40 @@ do\n \t\"\n done\n \n+test_diff_funcname () {\n+\tdesc=$1\n+\tcat <&8 >arg.header &&\n+\tcat <&9 >arg.test &&\n+\twhat=$(cat arg.what) &&\n+\n+\ttest_expect_success \"setup: $desc\" '\n+\t\tcp arg.test \"$what\" &&\n+\t\tcp arg.header expected &&\n+\t\tgit add \"$what\" &&\n+\t\tdo_change_me \"$what\"\n+\t' &&\n+\n+\ttest_expect_success \"$desc\" '\n+\t\tgit diff -U1 \"$what\" >diff &&\n+\t\tlast_diff_context_line diff >actual &&\n+\t\ttest_cmp expected actual\n+\t'\n+}\n+\n+for what in $diffpatterns\n+do\n+\ttest=\"$TEST_DIRECTORY/t4018/$what.sh\"\n+\tif ! test -e \"$test\"\n+\tthen\n+\t\tcontinue\n+\tfi &&\n+\n+\ttest_expect_success \"setup: hunk header for $what\" '\n+\t\techo \"$what diff=$what\" >.gitattributes &&\n+\t\techo \"$what\" >arg.what\n+\t' &&\n+\n+\t. \"$test\"\n+done\n+\n test_done\ndiff --git a/t/t4018/README b/t/t4018/README\nindex 0a246bbc10e..54ae735d5f8 100644\n--- a/t/t4018/README\n+++ b/t/t4018/README\n@@ -1,13 +1,45 @@\n-t4018 header: description of the test.\n+t4018 header: There are two ways of writing tests in this directory. In both cases\n How to write test cases\n =======================\n \n-Create test cases called \"LANG-whatever\" in this directory, where\n-\"LANG\" is e.g. the userdiff driver name, where \"whatever\" is a brief\n-description of the test.\n+There are two ways of writing tests in this directory. In both cases\n+\"LANG\" is the userdiff driver name, e.g. \"perl\" or \"cpp\".\n+\n+The word \"ChangeMe\" (exactly this form) should appear at a distance of\n+at least two lines from the line that must appear in the hunk\n+header. See below sections.\n+\n+t4018 header: t/README.\n+\"LANG.sh\" test cases\n+====================\n+\n+These tests use the normal test-lib.sh syntax and environment, and are\n+sourced by t4018-diff-funcname.sh. The \"test_diff_funcname()\" function\n+is a thin wrapper around the \"test_expect_success()\" function. See\n+t/README.\n+\n+The content of the \"EOF_TEST\" argument is used as-is, with the\n+exception of the \"ChangeMe\" token discussed above.\n+\n+The advantage of using this over the \"LANG-whatever\" test cases (see\n+below) are:\n \n-Insert the word \"ChangeMe\" (exactly this form) at a distance of\n-at least two lines from the line that must appear in the hunk header.\n+ - The ability to do custom test setup/teardown, e.g. using\n+   \"test_config\" before the test is run.\n+\n+ - The description of the test is a string, and doesn't need to be a\n+   valid filename.\n+\n+ - All the tests for a given driver are present in one file. As\n+   demonstrated with the two \"t4018 header\" lines in this file this is\n+   also possible with the \"LANG-whatever\" tests, but those N tests in\n+   one file won't benefit from different test descriptions.\n+\n+\"LANG-whatever\" test cases\n+==========================\n+\n+Create test cases called \"LANG-whatever\" in this directory, where\n+\"whatever\" is a brief description of the test.\n \n Any line starting with \"t4018\" is a control line for the test:\n \ndiff --git a/t/t4018/custom1-pattern b/t/t4018/custom1.sh\nold mode 100644\nnew mode 100755\nsimilarity index 71%\nrename from t/t4018/custom1-pattern\nrename to t/t4018/custom1.sh\nindex 37a3422384b..f8bbccadb47\n--- a/t/t4018/custom1-pattern\n+++ b/t/t4018/custom1.sh\n@@ -1,4 +1,12 @@\n-t4018 header: int special, RIGHT;\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'custom1: pattern' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+int special, RIGHT;\n+EOF_HUNK\n public class Beer\n {\n \tint special, RIGHT;\n@@ -16,3 +24,4 @@ public class Beer\n \t\t\t+ \"99 bottles of beer on the wall.\\n\");\n \t}\n }\n+EOF_TEST\ndiff --git a/t/t4018/custom2-match-to-end-of-line b/t/t4018/custom2-match-to-end-of-line\ndeleted file mode 100644\nindex 4800bb1c568..00000000000\n--- a/t/t4018/custom2-match-to-end-of-line\n+++ /dev/null\n@@ -1,9 +0,0 @@\n-t4018 header: RIGHT_Beer\n-public class RIGHT_Beer\n-{\n-\tint special;\n-\tpublic static void main(String args[])\n-\t{\n-\t\tSystem.out.print(\"ChangeMe\");\n-\t}\n-}\ndiff --git a/t/t4018/custom2.sh b/t/t4018/custom2.sh\nnew file mode 100755\nindex 00000000000..c68421f788e\n--- /dev/null\n+++ b/t/t4018/custom2.sh\n@@ -0,0 +1,18 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'custom2: match to end of line' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT_Beer\n+EOF_HUNK\n+public class RIGHT_Beer\n+{\n+\tint special;\n+\tpublic static void main(String args[])\n+\t{\n+\t\tSystem.out.print(\"ChangeMe\");\n+\t}\n+}\n+EOF_TEST\ndiff --git a/t/t4018/custom3-alternation-in-pattern b/t/t4018/custom3.sh\nold mode 100644\nnew mode 100755\nsimilarity index 66%\nrename from t/t4018/custom3-alternation-in-pattern\nrename to t/t4018/custom3.sh\nindex bf7df3d9a73..07c5c134ffe\n--- a/t/t4018/custom3-alternation-in-pattern\n+++ b/t/t4018/custom3.sh\n@@ -1,4 +1,12 @@\n-t4018 header: public static void main(String RIGHT[])\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'custom3: alternation in pattern' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+public static void main(String RIGHT[])\n+EOF_HUNK\n public class Beer\n {\n \tint special;\n@@ -16,3 +24,4 @@ public class Beer\n \t\t\t+ \"99 bottles of beer on the wall.\\n\");\n \t}\n }\n+EOF_TEST\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"417739","messageId":"20210224195129.4004-30-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"[PATCH v3 29/35] userdiff tests: test for a bug in 1dbf0c0ad6c","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-24T19:51:23Z","receivedAt":"2021-02-24T19:56:40Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a test for a bug in 1dbf0c0ad6c (userdiff: add built-in pattern\nfor golang, 2018-03-01), we'd ignore everything after the \"{\" only for\n\"type\" lines, but not \"func\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018/golang | 14 ++++++++++++++\n 1 file changed, 14 insertions(+)\n\ndiff --git a/t/t4018/golang b/t/t4018/golang\nindex 000e66b1c7b..70bf0d936bb 100644\n--- a/t/t4018/golang\n+++ b/t/t4018/golang\n@@ -16,6 +16,13 @@ func RIGHT() {\n \tb := ChangeMe\n }\n \n+t4018 description: func with comment after {\n+t4018 header: func name() { // comment\n+func name() { // comment\n+\ta := 5\n+\tb := ChangeMe\n+}\n+\n t4018 description: interface\n t4018 header: type RIGHT interface {\n type RIGHT interface {\n@@ -37,3 +44,10 @@ type RIGHT struct {\n \ta Type\n \tb ChangeMe\n }\n+\n+t4018 description: struct with comment after {\n+t4018 header: type some struct {\n+type some struct { // comment\n+\ta Type\n+\tb ChangeMe\n+}\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"417740","messageId":"20210224195129.4004-18-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"[PATCH v3 17/35] userdiff tests: move custom patterns into one test file","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-24T19:51:11Z","receivedAt":"2021-02-24T19:56:51Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"In a preceding commit the test infrastructure got rewritten so\n\"t/t4018/\" are now normal test files which can do things like set\nconfig, so let's make it responsible for setting up and tearing down\nthe config for its tests.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018-diff-funcname.sh | 16 +-------\n t/t4018/custom.sh        | 79 ++++++++++++++++++++++++++++++++++++++++\n t/t4018/custom1.sh       | 27 --------------\n t/t4018/custom2.sh       | 18 ---------\n t/t4018/custom3.sh       | 27 --------------\n 5 files changed, 80 insertions(+), 87 deletions(-)\n create mode 100755 t/t4018/custom.sh\n delete mode 100755 t/t4018/custom1.sh\n delete mode 100755 t/t4018/custom2.sh\n delete mode 100755 t/t4018/custom3.sh\n\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex 496313fc900..71a7b474cd4 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -15,18 +15,6 @@ test_expect_success 'setup' '\n \tsort <builtin-drivers >builtin-drivers.sorted &&\n \ttest_cmp builtin-drivers.sorted builtin-drivers &&\n \n-\t# a non-trivial custom pattern\n-\tgit config diff.custom1.funcname \"!static\n-!String\n-[^ \t].*s.*\" &&\n-\n-\t# a custom pattern which matches to end of line\n-\tgit config diff.custom2.funcname \"......Beer\\$\" &&\n-\n-\t# alternation in pattern\n-\tgit config diff.custom3.funcname \"Beer$\" &&\n-\tgit config diff.custom3.xfuncname \"^[ \t]*((public|static).*)$\" &&\n-\n \t# for regexp compilation tests\n \techo A >A.java &&\n \techo B >B.java\n@@ -34,9 +22,7 @@ test_expect_success 'setup' '\n \n diffpatterns=\"\n \t$(cat builtin-drivers)\n-\tcustom1\n-\tcustom2\n-\tcustom3\n+\tcustom\n \"\n \n for p in $diffpatterns\ndiff --git a/t/t4018/custom.sh b/t/t4018/custom.sh\nnew file mode 100755\nindex 00000000000..59d855c01c5\n--- /dev/null\n+++ b/t/t4018/custom.sh\n@@ -0,0 +1,79 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_expect_success 'custom: setup non-trivial custom' '\n+\tgit config diff.custom.funcname \"!static\n+!String\n+[^ \t].*s.*\"\n+'\n+\n+test_diff_funcname 'custom: non-trivial custom pattern' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+int special, RIGHT;\n+EOF_HUNK\n+public class Beer\n+{\n+\tint special, RIGHT;\n+\tpublic static void main(String args[])\n+\t{\n+\t\tString s=\" \";\n+\t\tfor(int x = 99; x > 0; x--)\n+\t\t{\n+\t\t\tSystem.out.print(x + \" bottles of beer on the wall \"\n+\t\t\t\t+ x + \" bottles of beer\\n\" // ChangeMe\n+\t\t\t\t+ \"Take one down, pass it around, \" + (x - 1)\n+\t\t\t\t+ \" bottles of beer on the wall.\\n\");\n+\t\t}\n+\t\tSystem.out.print(\"Go to the store, buy some more,\\n\"\n+\t\t\t+ \"99 bottles of beer on the wall.\\n\");\n+\t}\n+}\n+EOF_TEST\n+\n+test_expect_success 'custom: setup match to end of line' '\n+\tgit config diff.custom.funcname \"......Beer\\$\"\n+'\n+\n+test_diff_funcname 'custom: match to end of line' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+RIGHT_Beer\n+EOF_HUNK\n+public class RIGHT_Beer\n+{\n+\tint special;\n+\tpublic static void main(String args[])\n+\t{\n+\t\tSystem.out.print(\"ChangeMe\");\n+\t}\n+}\n+EOF_TEST\n+\n+test_expect_success 'custom: setup alternation in pattern' '\n+\tgit config diff.custom.funcname \"Beer$\" &&\n+\tgit config diff.custom.xfuncname \"^[ \t]*((public|static).*)$\"\n+'\n+\n+test_diff_funcname 'custom: alternation in pattern' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+public static void main(String RIGHT[])\n+EOF_HUNK\n+public class Beer\n+{\n+\tint special;\n+\tpublic static void main(String RIGHT[])\n+\t{\n+\t\tString s=\" \";\n+\t\tfor(int x = 99; x > 0; x--)\n+\t\t{\n+\t\t\tSystem.out.print(x + \" bottles of beer on the wall \"\n+\t\t\t\t+ x + \" bottles of beer\\n\" // ChangeMe\n+\t\t\t\t+ \"Take one down, pass it around, \" + (x - 1)\n+\t\t\t\t+ \" bottles of beer on the wall.\\n\");\n+\t\t}\n+\t\tSystem.out.print(\"Go to the store, buy some more,\\n\"\n+\t\t\t+ \"99 bottles of beer on the wall.\\n\");\n+\t}\n+}\n+EOF_TEST\ndiff --git a/t/t4018/custom1.sh b/t/t4018/custom1.sh\ndeleted file mode 100755\nindex f8bbccadb47..00000000000\n--- a/t/t4018/custom1.sh\n+++ /dev/null\n@@ -1,27 +0,0 @@\n-#!/bin/sh\n-#\n-# See ../t4018-diff-funcname.sh's test_diff_funcname()\n-#\n-\n-test_diff_funcname 'custom1: pattern' \\\n-\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n-int special, RIGHT;\n-EOF_HUNK\n-public class Beer\n-{\n-\tint special, RIGHT;\n-\tpublic static void main(String args[])\n-\t{\n-\t\tString s=\" \";\n-\t\tfor(int x = 99; x > 0; x--)\n-\t\t{\n-\t\t\tSystem.out.print(x + \" bottles of beer on the wall \"\n-\t\t\t\t+ x + \" bottles of beer\\n\" // ChangeMe\n-\t\t\t\t+ \"Take one down, pass it around, \" + (x - 1)\n-\t\t\t\t+ \" bottles of beer on the wall.\\n\");\n-\t\t}\n-\t\tSystem.out.print(\"Go to the store, buy some more,\\n\"\n-\t\t\t+ \"99 bottles of beer on the wall.\\n\");\n-\t}\n-}\n-EOF_TEST\ndiff --git a/t/t4018/custom2.sh b/t/t4018/custom2.sh\ndeleted file mode 100755\nindex c68421f788e..00000000000\n--- a/t/t4018/custom2.sh\n+++ /dev/null\n@@ -1,18 +0,0 @@\n-#!/bin/sh\n-#\n-# See ../t4018-diff-funcname.sh's test_diff_funcname()\n-#\n-\n-test_diff_funcname 'custom2: match to end of line' \\\n-\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n-RIGHT_Beer\n-EOF_HUNK\n-public class RIGHT_Beer\n-{\n-\tint special;\n-\tpublic static void main(String args[])\n-\t{\n-\t\tSystem.out.print(\"ChangeMe\");\n-\t}\n-}\n-EOF_TEST\ndiff --git a/t/t4018/custom3.sh b/t/t4018/custom3.sh\ndeleted file mode 100755\nindex 07c5c134ffe..00000000000\n--- a/t/t4018/custom3.sh\n+++ /dev/null\n@@ -1,27 +0,0 @@\n-#!/bin/sh\n-#\n-# See ../t4018-diff-funcname.sh's test_diff_funcname()\n-#\n-\n-test_diff_funcname 'custom3: alternation in pattern' \\\n-\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n-public static void main(String RIGHT[])\n-EOF_HUNK\n-public class Beer\n-{\n-\tint special;\n-\tpublic static void main(String RIGHT[])\n-\t{\n-\t\tString s=\" \";\n-\t\tfor(int x = 99; x > 0; x--)\n-\t\t{\n-\t\t\tSystem.out.print(x + \" bottles of beer on the wall \"\n-\t\t\t\t+ x + \" bottles of beer\\n\" // ChangeMe\n-\t\t\t\t+ \"Take one down, pass it around, \" + (x - 1)\n-\t\t\t\t+ \" bottles of beer on the wall.\\n\");\n-\t\t}\n-\t\tSystem.out.print(\"Go to the store, buy some more,\\n\"\n-\t\t\t+ \"99 bottles of beer on the wall.\\n\");\n-\t}\n-}\n-EOF_TEST\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"417741","messageId":"20210224195129.4004-19-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"[PATCH v3 18/35] userdiff tests: remove hack for \"RIGHT\" token","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-24T19:51:12Z","receivedAt":"2021-02-24T19:57:01Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Now that the \"RIGHT\" token isn't how we select the desired hunk header\nline in the test anymore we can revert a hack added in\nf1b75fbaf1 (t4018: convert custom pattern test to the new\ninfrastructure, 2014-03-21) and go back to the regular expression we\nwere testing before that change.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018/custom.sh | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/t/t4018/custom.sh b/t/t4018/custom.sh\nindex 59d855c01c5..b208a771d28 100755\n--- a/t/t4018/custom.sh\n+++ b/t/t4018/custom.sh\n@@ -33,14 +33,14 @@ public class Beer\n EOF_TEST\n \n test_expect_success 'custom: setup match to end of line' '\n-\tgit config diff.custom.funcname \"......Beer\\$\"\n+\tgit config diff.custom.funcname \"Beer\\$\"\n '\n \n test_diff_funcname 'custom: match to end of line' \\\n \t8<<\\EOF_HUNK 9<<\\EOF_TEST\n-RIGHT_Beer\n+Beer\n EOF_HUNK\n-public class RIGHT_Beer\n+public class Beer\n {\n \tint special;\n \tpublic static void main(String args[])\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"417742","messageId":"20210224195129.4004-23-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"[PATCH v3 22/35] gitattributes doc: reword discussion of built-in userdiff patterns","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-24T19:51:16Z","receivedAt":"2021-02-24T19:57:16Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Reword the discussion of the built-in userdiff patterns to make it\nmore natural to precede it with a discussion about the semantics of\npattern matching, instead of assuming that it follows right after the\n\"diff.tex.xfuncname\" example which now immediately precedes it. This\nwill make a follow-up commit smaller.\n\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/gitattributes.txt | 16 +++++++++++-----\n 1 file changed, 11 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\nindex e84e104f932..62c1147ba97 100644\n--- a/Documentation/gitattributes.txt\n+++ b/Documentation/gitattributes.txt\n@@ -794,11 +794,17 @@ backslashes; the pattern above picks a line that begins with a\n backslash, and zero or more occurrences of `sub` followed by\n `section` followed by open brace, to the end of line.\n \n-There are a few built-in patterns to make this easier, and `tex`\n-is one of them, so you do not have to write the above in your\n-configuration file (you still need to enable this with the\n-attribute mechanism, via `.gitattributes`).  The following built in\n-patterns are available:\n+There are built-in patterns shipped as part of git itself. A more\n+advanced version of the `tex` pattern discussed above is one of them.\n+\n+For built-in patterns, you do not need `diff.<lang>.xfuncname` in your\n+configuration file as discussed above, but if present, it will\n+override a built-in pattern.\n+\n+Nevertheless, you need to enable built-in patterns via .gitattributes`\n+for the pattern to take effect.\n+\n+The following built-in patterns are available:\n \n - `ada` suitable for source code in the Ada language.\n \n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"417743","messageId":"20210224195129.4004-21-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"[PATCH v3 20/35] userdiff tests: assert that new built-in drivers have tests","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-24T19:51:14Z","receivedAt":"2021-02-24T19:57:24Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add an assertion to the userdiff test framework to check that\neverything except a narrow whitelist of existing built-in patterns has\ntests.\n\nSince this test framework was added we've added new patterns without\nany tests. Let's make it obvious in the future in the diff for such\npatches that they should have those tests.\n\nFor anything with tests we can skip the \"does the pattern compile?\"\ntest, as the actual tests will check that for us.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018-diff-funcname.sh | 30 +++++++++++++++++++++++++++++-\n 1 file changed, 29 insertions(+), 1 deletion(-)\n\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex b80546b4d7f..a3058fda130 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -15,6 +15,19 @@ test_expect_success 'setup' '\n \tsort <builtin-drivers >builtin-drivers.sorted &&\n \ttest_cmp builtin-drivers.sorted builtin-drivers &&\n \n+\t# Do not add anything to this list. New built-in drivers should have\n+\t# tests\n+\tcat >drivers-no-tests <<-\\EOF &&\n+\tada\n+\tbibtex\n+\tcsharp\n+\thtml\n+\tobjc\n+\tpascal\n+\truby\n+\ttex\n+\tEOF\n+\n \t# for regexp compilation tests\n \techo A >A.java &&\n \techo B >B.java\n@@ -22,7 +35,12 @@ test_expect_success 'setup' '\n \n for p in $(cat builtin-drivers)\n do\n-\ttest_expect_success \"builtin $p pattern compiles\" '\n+\tP=$(echo $p | tr 'a-z' 'A-Z')\n+\tif grep -q $p drivers-no-tests\n+\tthen\n+\t\ttest_set_prereq NO_TEST_FOR_DRIVER_$P\n+\tfi\n+\ttest_expect_success NO_TEST_FOR_DRIVER_$P \"builtin $p pattern compiles\" '\n \t\techo \"*.java diff=$p\" >.gitattributes &&\n \t\ttest_expect_code 1 git diff --no-index \\\n \t\t\tA.java B.java 2>msg &&\n@@ -119,11 +137,17 @@ test_diff_funcname () {\n \t'\n }\n \n+>drivers-had-no-tests\n for what in $diffpatterns\n do\n \ttest=\"$TEST_DIRECTORY/t4018/$what.sh\"\n \tif ! test -e \"$test\"\n \tthen\n+\t\tgit -C t4018 ls-files ':!*.sh' \"$what*\" >other-tests &&\n+\t\tif ! test -s other-tests\n+\t\tthen\n+\t\t\techo $what >>drivers-had-no-tests\n+\t\tfi\n \t\tcontinue\n \tfi &&\n \n@@ -135,4 +159,8 @@ do\n \t. \"$test\"\n done\n \n+test_expect_success 'we should not have new built-in drivers without tests' '\n+\ttest_cmp drivers-no-tests drivers-had-no-tests\n+'\n+\n test_done\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"417744","messageId":"20210224195129.4004-27-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"[PATCH v3 26/35] userdiff tests: switch to -U0 by default","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-24T19:51:20Z","receivedAt":"2021-02-24T19:57:46Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the -U1 default in the userdiff tests to -U0. There's no reason\nto use -U1, it just causes confusion and the need to work around the\ntest semantics themselves by not having the matching line immediately\nprecede the changed line.\n\nThe -U1 was initially added in f12c66b9bb8 (userdiff/perl: anchor\n\"sub\" and \"package\" patterns on the left, 2011-05-21), seemingly to\nappease a specific test I'd reported as failing with the \"perl\"\npattern. This was then documented in bfa7d01413b (t4018: an\ninfrastructure to test hunk headers, 2014-03-21).\n\nThere's no special logic in xdiff/xemit.c that would depend on -U1\nv.s. -U0 here. So let's use -U0 for simplicity, we're interested in\nwhat lines match most of the time, not at which line we start the\nsearch.\n\nA couple of test cases depended on the -U1, most confusingly one of\nthe custom.sh test cases.\n\nLet's extend \"test_diff_funcname()\" to take an argument to \"git diff\"\nand test \"-U1\" there, both because rewriting the testcase would be\npainful to understand in the context of reviewing this change, and so\nthat we explicitly test that we're using -U0.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018-diff-funcname.sh | 5 +++--\n t/t4018/README           | 7 +++----\n t/t4018/custom.sh        | 1 +\n t/t4018/perl.sh          | 1 -\n 4 files changed, 7 insertions(+), 7 deletions(-)\n\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex a3058fda130..ca23d156666 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -104,7 +104,7 @@ do\n \t\"\n \n \ttest_expect_success \"hunk header: $i\" \"\n-\t\tgit diff -U1 $i >diff &&\n+\t\tgit diff -U0 $i >diff &&\n \t\tlast_diff_context_line diff >ctx &&\n \t\ttest_cmp t4018/$i.header ctx\n \t\"\n@@ -112,6 +112,7 @@ done\n \n test_diff_funcname () {\n \tdesc=$1\n+\tdiff_opts=${2:--U0} &&\n \tcat <&8 >arg.header &&\n \tcat <&9 >arg.test &&\n \twhat=$(cat arg.what) &&\n@@ -124,7 +125,7 @@ test_diff_funcname () {\n \t' &&\n \n \ttest_expect_success \"$desc\" '\n-\t\tgit diff -U1 \"$what\" >diff &&\n+\t\tgit diff $diff_opts \"$what\" >diff &&\n \t\tlast_diff_context_line diff >actual &&\n \t\ttest_cmp expected actual\n \t' &&\ndiff --git a/t/t4018/README b/t/t4018/README\nindex a3220dd6374..cef7d3c0e17 100644\n--- a/t/t4018/README\n+++ b/t/t4018/README\n@@ -5,11 +5,10 @@ How to write test cases\n There are two ways of writing tests in this directory. In both cases\n \"LANG\" is the userdiff driver name, e.g. \"perl\" or \"cpp\".\n \n-The word \"ChangeMe\" (exactly this form) should appear at a distance of\n-at least two lines from the line that must appear in the hunk\n-header. See below sections.\n+The word \"ChangeMe\" below the line that must appear in the hunk header\n+(we run the diff with -U0). See below sections.\n \n-t4018 header: t/README.\n+t4018 header: The content of the \"EOF_TEST\" argument is used as-is, with the\n \"LANG.sh\" test cases\n ====================\n \ndiff --git a/t/t4018/custom.sh b/t/t4018/custom.sh\nindex 127524afda3..97f310c02fb 100755\n--- a/t/t4018/custom.sh\n+++ b/t/t4018/custom.sh\n@@ -10,6 +10,7 @@ test_expect_success 'custom: setup non-trivial custom' '\n '\n \n test_diff_funcname 'custom: non-trivial custom pattern' \\\n+\t'-U1' \\\n \t8<<\\EOF_HUNK 9<<\\EOF_TEST\n int special, RIGHT;\n EOF_HUNK\ndiff --git a/t/t4018/perl.sh b/t/t4018/perl.sh\nindex ba11241750b..63f373d1a43 100755\n--- a/t/t4018/perl.sh\n+++ b/t/t4018/perl.sh\n@@ -89,6 +89,5 @@ sub foo;\n EOF_HUNK\n sub foo;\n =head1\n-\n ChangeMe\n EOF_TEST\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"417745","messageId":"20210224195129.4004-25-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"[PATCH v3 24/35] userdiff tests: move away from \"RIGHT\" in perl.sh","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-24T19:51:18Z","receivedAt":"2021-02-24T19:57:46Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The \"RIGHT\" token is no longer magical, see recent changes to\nt/t4018/README. Let's change it in the recently moved perl.sh\ntests. This change was done separately so the earlier commit could\nbenefit from the \"diff --color-moved\" detection.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018/perl.sh | 20 ++++++++++----------\n 1 file changed, 10 insertions(+), 10 deletions(-)\n\ndiff --git a/t/t4018/perl.sh b/t/t4018/perl.sh\nindex ac8fff7417a..b53b759353b 100755\n--- a/t/t4018/perl.sh\n+++ b/t/t4018/perl.sh\n@@ -5,9 +5,9 @@\n \n test_diff_funcname 'perl: skip end of heredoc' \\\n \t8<<\\EOF_HUNK 9<<\\EOF_TEST\n-sub RIGHTwithheredocument {\n+sub withheredocument {\n EOF_HUNK\n-sub RIGHTwithheredocument {\n+sub withheredocument {\n \tprint <<\"EOF\"\n decoy here-doc\n EOF\n@@ -19,9 +19,9 @@ EOF_TEST\n \n test_diff_funcname 'perl: skip forward decl' \\\n \t8<<\\EOF_HUNK 9<<\\EOF_TEST\n-package RIGHT;\n+package Some::Package;\n EOF_HUNK\n-package RIGHT;\n+package Some::Package;\n \n use strict;\n use warnings;\n@@ -35,13 +35,13 @@ EOF_TEST\n \n test_diff_funcname 'perl: skip sub in pod' \\\n \t8<<\\EOF_HUNK 9<<\\EOF_TEST\n-=head1 SYNOPSIS_RIGHT\n+=head1 SYNOPSIS\n EOF_HUNK\n =head1 NAME\n \n Beer - subroutine to output fragment of a drinking song\n \n-=head1 SYNOPSIS_RIGHT\n+=head1 SYNOPSIS\n \n \tuse Beer qw(round finalround);\n \n@@ -59,9 +59,9 @@ EOF_TEST\n \n test_diff_funcname 'perl: sub definition' \\\n \t8<<\\EOF_HUNK 9<<\\EOF_TEST\n-sub RIGHT {\n+sub asub {\n EOF_HUNK\n-sub RIGHT {\n+sub asub {\n \tmy ($n) = @_;\n \tprint \"ChangeMe\";\n }\n@@ -69,9 +69,9 @@ EOF_TEST\n \n test_diff_funcname 'perl: sub definition kr brace' \\\n \t8<<\\EOF_HUNK 9<<\\EOF_TEST\n-sub RIGHT\n+sub asub\n EOF_HUNK\n-sub RIGHT\n+sub asub\n {\n \tprint \"ChangeMe\\n\";\n }\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"417746","messageId":"20210224195129.4004-29-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"[PATCH v3 28/35] userdiff tests: assert empty hunk header context on -U<large>","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-24T19:51:22Z","receivedAt":"2021-02-24T19:58:26Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Assert the existing behavior that under -U<large> we'll show no hunk\nheader context, where <large> takes us past the potential hunk header\nwe'd have extracted.\n\nI'm just picking a number over nine thousand as a really large number\nwe're unlikely to exceed in these tests.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018-diff-funcname.sh | 7 +++++++\n 1 file changed, 7 insertions(+)\n\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex ca23d156666..ba10d1f5313 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -130,6 +130,13 @@ test_diff_funcname () {\n \t\ttest_cmp expected actual\n \t' &&\n \n+\ttest_expect_success \"$desc -U9001\" '\n+\t\tgit diff -U9001 \"$what\" >diff &&\n+\t\tlast_diff_context_line diff >actual &&\n+\t\techo >blank &&\n+\t\ttest_cmp blank actual\n+\t' &&\n+\n \ttest_expect_success \"teardown: $desc\" '\n \t\t# In case any custom config was set immediately before\n \t\t# the test itself in the test file\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"417747","messageId":"20210224195129.4004-32-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"[PATCH v3 31/35] userdiff golang: don't over-match intented constructs","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-24T19:51:25Z","receivedAt":"2021-02-24T19:58:46Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a bug introduced when the \"golang\" driver was added in\n1dbf0c0ad6c (userdiff: add built-in pattern for golang, 2018-03-01).\n\nUnlike the default def_ff() driver in xemit.c it would match \"type\"\ndeclarations inside functions. Let's make it mandatory that a \"func\"\nor \"type\" must be at the beginning of the line with no whitespace to\nget around this.\n\nGo is such a regularly formatted language that I think this can be\ncounted on. I think the whitespace matching was probably copy/pasted\nfrom an earlier userdiff.c pattern.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018/golang | 20 ++++++++++++++++++++\n userdiff.c     |  4 ++--\n 2 files changed, 22 insertions(+), 2 deletions(-)\n\ndiff --git a/t/t4018/golang b/t/t4018/golang\nindex 72a35d66008..252b6049da4 100644\n--- a/t/t4018/golang\n+++ b/t/t4018/golang\n@@ -51,3 +51,23 @@ type some struct { // comment\n \ta Type\n \tb ChangeMe\n }\n+\n+t4018 description: func combined with type\n+t4018 header: func myfunc() {\n+func myfunc() {\n+\ttype mystruct struct {\n+\t\ta Foo\n+\t\tb Bar\n+\t}\n+\tChangeMe\n+\n+t4018 description: anonymous indented func()\n+t4018 header: func SomeThing() bool {\n+func SomeThing() bool {\n+\tfunc() {\n+\t\tdefer func() {\n+\t\t\tfmt.Println(\"hello\")\n+\t\t}()\n+\t}()\n+\n+\tChangeMe\ndiff --git a/userdiff.c b/userdiff.c\nindex 698eca5ad35..704af241e44 100644\n--- a/userdiff.c\n+++ b/userdiff.c\n@@ -129,9 +129,9 @@ IPATTERN(\"fountain\",\n \t \"[^ \\t-]+\"),\n PATTERNS(\"golang\",\n \t /* Functions */\n-\t \"^[ \\t]*(func[ \\t].*)\\n\"\n+\t \"^(func[ \\t].*)\\n\"\n \t /* Structs and interfaces */\n-\t \"^[ \\t]*(type[ \\t].*(struct|interface)[ \\t].*)\",\n+\t \"^(type[ \\t].*[ \\t](struct|interface)[ \\t].*)\",\n \t /* -- */\n \t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n \t \"|[-+0-9.eE]+i?|0[xX]?[0-9a-fA-F]+i?\"\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"417748","messageId":"20210224195129.4004-34-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"[PATCH v3 33/35] userdiff golang: match multi-line \"const\" and \"import\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-24T19:51:27Z","receivedAt":"2021-02-24T19:58:57Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add matching rules for the very common pattern of having a multi-line\n\"import\" or \"const\" declaration near the start of the \"package\"\nline. Before this change we'd skip this and match whatever came before\nit, e.g. the \"package\" line.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018/golang | 26 ++++++++++++++++++++++++++\n userdiff.c     |  2 ++\n 2 files changed, 28 insertions(+)\n\ndiff --git a/t/t4018/golang b/t/t4018/golang\nindex 38f254cd269..7f51fa02203 100644\n--- a/t/t4018/golang\n+++ b/t/t4018/golang\n@@ -47,6 +47,32 @@ package camelCase\n import \"fmt\"\n // ChangeMe\n \n+t4018 description: import (\n+t4018 header: import (\n+package somePackage\n+\n+import (\n+\t\"os\"\n+\tChangeMe\n+)\n+\n+t4018 description: const (\n+t4018 header: const (\n+package somePackage\n+\n+const (\n+\tFoo = 1\n+\tBar = ChangeMe\n+)\n+\n+t4018 description: const rule is selective\n+t4018 header: package main\n+package main\n+\n+const Foo = \"Bar\"\n+\n+// ChangeMe\n+\n t4018 description: complex function\n t4018 header: func (t *Test) RIGHT(a Type) (Type, error) {\n type Test struct {\ndiff --git a/userdiff.c b/userdiff.c\nindex bbbbfa33e0a..c4a2bfaed70 100644\n--- a/userdiff.c\n+++ b/userdiff.c\n@@ -132,6 +132,8 @@ PATTERNS(\"golang\",\n \t \"^(package[ \\t][a-z][A-Za-z0-9_]+)[ \\t]*\\n\"\n \t /* Functions */\n \t \"^(func[ \\t].*)\\n\"\n+\t /* const & import */\n+\t \"^((import|const)[ \\t]*\\\\([ \\t]*)\\n\"\n \t /* Structs and interfaces */\n \t \"^(type[ \\t].*[ \\t](struct|interface)[ \\t].*)\",\n \t /* -- */\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"417749","messageId":"20210224195129.4004-35-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"[PATCH v3 34/35] userdiff tests: add basic test for ada","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-24T19:51:28Z","receivedAt":"2021-02-24T19:58:57Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add test for the ada userdiff pattern added in e90d065e64 (Add\nuserdiff patterns for Ada, 2012-09-16).\n\nI don't know the ada language itself, I just stole a couple of\nexamples of code that used tokens we're matching[1][2]. Both test\nexamples stress our negative and positive matching rules.\n\n1. https://rosettacode.org/wiki/99_bottles_of_beer#Ada\n2. https://en.wikibooks.org/wiki/Ada_Programming/Tasking\n---\n t/t4018-diff-funcname.sh |  1 -\n t/t4018/ada.sh           | 37 +++++++++++++++++++++++++++++++++++++\n 2 files changed, 37 insertions(+), 1 deletion(-)\n create mode 100755 t/t4018/ada.sh\n\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex ba10d1f5313..b0c2782d067 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -18,7 +18,6 @@ test_expect_success 'setup' '\n \t# Do not add anything to this list. New built-in drivers should have\n \t# tests\n \tcat >drivers-no-tests <<-\\EOF &&\n-\tada\n \tbibtex\n \tcsharp\n \thtml\ndiff --git a/t/t4018/ada.sh b/t/t4018/ada.sh\nnew file mode 100755\nindex 00000000000..45fc2c7a3b2\n--- /dev/null\n+++ b/t/t4018/ada.sh\n@@ -0,0 +1,37 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'ada: \"procedure\" over \"with\"' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+procedure Bottles is\n+EOF_HUNK\n+with Ada.Text_Io; use Ada.Text_Io;\n+ procedure Bottles is\n+ begin\n+    for X in reverse 1..99 loop\n+       Put_Line(Integer'Image(X) & \" bottles of beer on the wall\");\n+       Put_Line(Integer'Image(X) & \" bottles of beer\"); -- ChangeMe\n+       Put_Line(\"Take one down, pass it around\");\n+       Put_Line(Integer'Image(X - 1) & \" bottles of beer on the wall\");\n+       New_Line;\n+    end loop;\n+ end Bottles;\n+EOF_TEST\n+\n+test_diff_funcname 'ada: \"task\" over \"procedure\"' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+task body Check_CPU is\n+EOF_HUNK\n+procedure Housekeeping is\n+  task Check_CPU;\n+  task Backup_Disk;\n+\n+  task body Check_CPU is\n+    -- Comment for spacing with\n+    -- the above \"task\" for -U1\n+    ChangeMe\n+  end Check_CPU;\n+end Housekeeping;\n+EOF_TEST\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"417750","messageId":"20210224195129.4004-33-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"[PATCH v3 32/35] userdiff golang: add a rule to match \"package\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-24T19:51:26Z","receivedAt":"2021-02-24T19:58:57Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Improve the \"golang\" built-in pattern to match \"package\" lines, as\nthey weren't matched before changing e.g. the imports would commonly\nresult in an empty hunk header, now we'll instead show the package\nname.\n\nI used https://blog.golang.org/package-names as a guide here, but\ne.g. \"foo_bar\" is still valid syntax, so let's let it pass but veer on\nthe side of not having false positives.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018/golang | 49 +++++++++++++++++++++++++++++++++++++++++++++++++\n userdiff.c     |  2 ++\n 2 files changed, 51 insertions(+)\n\ndiff --git a/t/t4018/golang b/t/t4018/golang\nindex 252b6049da4..38f254cd269 100644\n--- a/t/t4018/golang\n+++ b/t/t4018/golang\n@@ -1,3 +1,52 @@\n+t4018 description: package\n+t4018 header: package main\n+package main\n+\n+import \"fmt\"\n+// ChangeMe\n+\n+t4018 description: package regex is selective -- ALLCAPS\n+t4018 header: package main\n+package ALLCAPS\n+\n+import \"fmt\"\n+// ChangeMe\n+\n+t4018 description: package regex is selective -- CamelCase\n+t4018 header: package main\n+package CamelCase\n+\n+import \"fmt\"\n+// ChangeMe\n+\n+t4018 description: package regex is selective -- 123\n+t4018 header: package main\n+package 123\n+\n+import \"fmt\"\n+// ChangeMe\n+\n+t4018 description: package regex is not overly selective -- x509\n+t4018 header: package x509\n+package x509\n+\n+import \"fmt\"\n+// ChangeMe\n+\n+t4018 description: package regex is not overly selective -- underbars\n+t4018 header: package not_conventional\n+package not_conventional\n+\n+import \"fmt\"\n+// ChangeMe\n+\n+t4018 description: package regex is not overly selective -- camelCase\n+t4018 header: package camelCase\n+package camelCase\n+\n+import \"fmt\"\n+// ChangeMe\n+\n t4018 description: complex function\n t4018 header: func (t *Test) RIGHT(a Type) (Type, error) {\n type Test struct {\ndiff --git a/userdiff.c b/userdiff.c\nindex 704af241e44..bbbbfa33e0a 100644\n--- a/userdiff.c\n+++ b/userdiff.c\n@@ -128,6 +128,8 @@ IPATTERN(\"fountain\",\n \t /* -- */\n \t \"[^ \\t-]+\"),\n PATTERNS(\"golang\",\n+\t /* Packages */\n+\t \"^(package[ \\t][a-z][A-Za-z0-9_]+)[ \\t]*\\n\"\n \t /* Functions */\n \t \"^(func[ \\t].*)\\n\"\n \t /* Structs and interfaces */\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"417751","messageId":"20210224195129.4004-36-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"[PATCH v3 35/35] userdiff tests: add basic test for ruby","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-24T19:51:29Z","receivedAt":"2021-02-24T19:59:00Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a test for the Ruby pattern added way back in ad8c1d9260 (diff:\nadd ruby funcname pattern, 2008-07-31).\n\nThe \"One/Two\" picking demonstrates existing behavior, and a general\ncase where we may not do what the user expects since we're not aware\nof the indentation level.\n\nThe code is modified from the Ruby code we have in-tree at\nDocumentation/asciidoctor-extensions.rb.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018-diff-funcname.sh |  1 -\n t/t4018/ruby.sh          | 58 ++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 58 insertions(+), 1 deletion(-)\n create mode 100755 t/t4018/ruby.sh\n\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex b0c2782d067..7793d7652d5 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -23,7 +23,6 @@ test_expect_success 'setup' '\n \thtml\n \tobjc\n \tpascal\n-\truby\n \ttex\n \tEOF\n \ndiff --git a/t/t4018/ruby.sh b/t/t4018/ruby.sh\nnew file mode 100755\nindex 00000000000..ef8a154421a\n--- /dev/null\n+++ b/t/t4018/ruby.sh\n@@ -0,0 +1,58 @@\n+#!/bin/sh\n+#\n+# See ../t4018-diff-funcname.sh's test_diff_funcname()\n+#\n+\n+test_diff_funcname 'ruby: \"def\" over \"class/module\"' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+def process(parent)\n+EOF_HUNK\n+require 'asciidoctor'\n+\n+module Git\n+  module Documentation\n+    class SomeClass\n+      use_some\n+\n+      def process(parent)\n+        puts(\"hello\")\n+\tputs(ChangeMe)\n+      end\n+    end\n+  end\n+end\n+EOF_TEST\n+\n+test_diff_funcname 'ruby: \"class\" over \"class/module\"' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+class Two\n+EOF_HUNK\n+module Git\n+  module Documentation\n+    class One\n+    end\n+\n+    class Two\n+      # Spacing for -U1\n+      ChangeMe\n+    end\n+  end\n+end\n+EOF_TEST\n+\n+test_diff_funcname 'ruby: picks first \"class/module/def\" before changed context' \\\n+\t'-U1' \\\n+\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n+class One\n+EOF_HUNK\n+module Git\n+  module Documentation\n+    class One\n+    end\n+\n+    class Two\n+      ChangeMe\n+    end\n+  end\n+end\n+EOF_TEST\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"417752","messageId":"20210224195129.4004-31-avarab@gmail.com","threadId":"55144","inReplyTo":"20210215154427.32693-1-avarab@gmail.com","subject":"[PATCH v3 30/35] userdiff golang: simplify and correct matching regex","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-24T19:51:24Z","receivedAt":"2021-02-24T19:59:41Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Simplify the regex for the golang driver added in\n1dbf0c0ad6c (userdiff: add built-in pattern for golang, 2018-03-01) to\nremove redundant constructs.\n\nThere's no point in having a regex like this:\n\n    .*(foo)?\n\nWhen we can just write:\n\n    .*\n\nIn the \"func\" case, since the \"(foo?)\" match isn't mandatory it won't\nmatter for the end result, and in this case we're not using the\ncapture pattern. Not that it would matter since it's followed by a\ngreedy .*, so we'd only get the empty string.\n\nIn the \"type\" case we would stop at the \"{\", since it was not preceded\nby a \".*\". This was a bug, if we have a comment or something else on\nthat line we should include it.\n\nI'm also changing the \"func[ \\t]*.*\" rule to \"func[ \\t].*\" while I'm\nat it. We should always get whitespace after \"func\", so this narrows\ndown our match. Let's do the same in the new \"type\" rule.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018/golang | 2 +-\n userdiff.c     | 4 ++--\n 2 files changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/t/t4018/golang b/t/t4018/golang\nindex 70bf0d936bb..72a35d66008 100644\n--- a/t/t4018/golang\n+++ b/t/t4018/golang\n@@ -46,7 +46,7 @@ type RIGHT struct {\n }\n \n t4018 description: struct with comment after {\n-t4018 header: type some struct {\n+t4018 header: type some struct { // comment\n type some struct { // comment\n \ta Type\n \tb ChangeMe\ndiff --git a/userdiff.c b/userdiff.c\nindex 55f4f769bd3..698eca5ad35 100644\n--- a/userdiff.c\n+++ b/userdiff.c\n@@ -129,9 +129,9 @@ IPATTERN(\"fountain\",\n \t \"[^ \\t-]+\"),\n PATTERNS(\"golang\",\n \t /* Functions */\n-\t \"^[ \\t]*(func[ \\t]*.*(\\\\{[ \\t]*)?)\\n\"\n+\t \"^[ \\t]*(func[ \\t].*)\\n\"\n \t /* Structs and interfaces */\n-\t \"^[ \\t]*(type[ \\t].*(struct|interface)[ \\t]*(\\\\{[ \\t]*)?)\",\n+\t \"^[ \\t]*(type[ \\t].*(struct|interface)[ \\t].*)\",\n \t /* -- */\n \t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n \t \"|[-+0-9.eE]+i?|0[xX]?[0-9a-fA-F]+i?\"\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"417776","messageId":"xmqqeeh4yibt.fsf@gitster.g","threadId":"55144","inReplyTo":"20210224195129.4004-12-avarab@gmail.com","subject":"Re: [PATCH v3 11/35] userdiff tests: match full hunk headers","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-02-25T02:47:18Z","receivedAt":"2021-02-25T02:48:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\n> index 9aec9f8e6de..15dcbe735ca 100755\n> --- a/t/t4018-diff-funcname.sh\n> +++ b/t/t4018-diff-funcname.sh\n> @@ -70,16 +70,20 @@ test_expect_success 'setup hunk header tests' '\n>  \t\techo \"$i-* diff=$i\"\n>  \tdone > .gitattributes &&\n>  \n> -\t# add all test files to the index\n> -\t(\n> -\t\tcd \"$TEST_DIRECTORY\"/t4018 &&\n> -\t\tgit --git-dir=\"$TRASH_DIRECTORY/.git\" add .\n> -\t) &&\n> -\n> -\t# place modified files in the worktree\n> -\tfor i in $(git ls-files)\n> +\tcp -R \"$TEST_DIRECTORY\"/t4018 . &&\n\nIs this because otherwise we'll mix leftover cruft in the source\ndirectory?  I guess this is OK as it is just once per t4018 test\nrun.\n\n> +\tgit init t4018 &&\n> +\tgit -C t4018 add . &&\n> +\n> +\tfor i in $(git -C t4018 ls-files)\n>  \tdo\n> -\t\tsed -e \"s/ChangeMe/IWasChanged/\" <\"$TEST_DIRECTORY/t4018/$i\" >\"$i\" || return 1\n> +\t\tgrep -v \"^t4018\" \"t4018/$i\" >\"t4018/$i.content\" &&\n> +\t\tsed -n -e \"s/^t4018 header: //p\" <\"t4018/$i\" >\"t4018/$i.header\" &&\n\nFor now this would do, but I am assuming that the \"t4018\" prefix\n(which by the way is a way to make it harder to renumber these\ntests) was invented so that we can add more magic than just\n\"header:\" in the futhre (either in this series or later).  I am not\nsure if we want to run one invocation of \"sed\" for each new magic we\ninvent---perhaps the plan is that we'd keep the framework simple for\nnow (while we have only one \"magic\") and then revamp it when we gain\nthe second one, in which case I am perfectly fine with it.\n\n> +\t\tcp \"t4018/$i.content\" \"$i\" &&\n> +\n> +\t\t# add test file to the index\n> +\t\tgit add \"$i\" &&\n> +\t\t# place modified file in the worktree\n> +\t\tsed -e \"s/ChangeMe/IWasChanged/\" <\"t4018/$i.content\" >\"$i\" || return 1\n\nOK.\n\n>  \tdone\n>  '\n>  \n> @@ -87,8 +91,9 @@ test_expect_success 'setup hunk header tests' '\n>  for i in $(git ls-files)\n>  do\n>  \ttest_expect_success \"hunk header: $i\" \"\n> -\t\tgit diff -U1 $i >actual &&\n> -\t\tgrep '@@ .* @@.*RIGHT' actual\n> +\t\tgit diff -U1 $i >diff &&\n> +\t\tsed -n -e 's/^.*@@$//p' -e 's/^.*@@ //p' <diff >ctx &&\n\nThe original was already loose but this makes it even looser.  Can\nwe tighten the pattern to strip the line number information from the\nhunk header to something more like like this, please?\n\n\t/^@@[-+0-9, ]*@@/\n\nThanks.\n"},{"id":"417777","messageId":"xmqqa6rsyi2u.fsf@gitster.g","threadId":"55144","inReplyTo":"20210224195129.4004-13-avarab@gmail.com","subject":"Re: [PATCH v3 12/35] userdiff tests: change setup loop to individual test setup","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-02-25T02:52:41Z","receivedAt":"2021-02-25T02:53:27Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> +# check each individual file\n> +for i in $(git -C t4018 ls-files)\n> +do\n> +\ttest_expect_success \"setup hunk header: $i\" \"\n> +\t\tgrep -v '^t4018' \\\"t4018/$i\\\" >\\\"t4018/$i.content\\\" &&\n> +\t\tsed -n -e 's/^t4018 header: //p' <\\\"t4018/$i\\\" >\\\"t4018/$i.header\\\" &&\n> +\t\tcp \\\"t4018/$i.content\\\" \\\"$i\\\" &&\n>  \n>  \t\t# add test file to the index\n> -\t\tgit add \"$i\" &&\n> +\t\tgit add \\\"$i\\\" &&\n>  \t\t# place modified file in the worktree\n> -\t\tsed -e \"s/ChangeMe/IWasChanged/\" <\"t4018/$i.content\" >\"$i\" || return 1\n> -\tdone\n> -'\n> +\t\tsed -e 's/ChangeMe/IWasChanged/' <\\\"t4018/$i.content\\\" >\\\"$i\\\"\n> +\t\"\n\nPlease use '' around the second argument (i.e. test body) of the\ntest_expect_success, and use \"\" inside it.  \"$i\" that is used in the\nloop is visible perfectly fine inside the test body when it is\neval'ed, and we won't have to count ugly backslashes that way.\n\n>  \n> -# check each individual file\n> -for i in $(git ls-files)\n> -do\n>  \ttest_expect_success \"hunk header: $i\" \"\n>  \t\tgit diff -U1 $i >diff &&\n>  \t\tsed -n -e 's/^.*@@$//p' -e 's/^.*@@ //p' <diff >ctx &&\n"},{"id":"417778","messageId":"xmqq5z2gyhun.fsf@gitster.g","threadId":"55144","inReplyTo":"20210224195129.4004-14-avarab@gmail.com","subject":"Re: [PATCH v3 13/35] userdiff tests: factor out test_diff_funcname() logic","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-02-25T02:57:36Z","receivedAt":"2021-02-25T02:58:38Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Factor out logic in test_diff_funcname() into two helper functions,\n> these will be useful in a follow-up commit where we'll do this munging\n> in more than one place.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  t/t4018-diff-funcname.sh | 16 +++++++++++++---\n>  1 file changed, 13 insertions(+), 3 deletions(-)\n>\n> diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\n> index 2365f0e361e..8a8a7a99c88 100755\n> --- a/t/t4018-diff-funcname.sh\n> +++ b/t/t4018-diff-funcname.sh\n> @@ -75,6 +75,17 @@ test_expect_success 'setup hunk header tests' '\n>  \tgit -C t4018 add .\n>  '\n>  \n> +do_change_me () {\n> +\tfile=$1\n> +\tsed -e \"s/ChangeMe/IWasChanged/\" <\"$file\" >tmp &&\n> +\tmv tmp \"$file\"\n> +}\n> +\n> +last_diff_context_line () {\n\nWhat this helper computes looks more like header line for each and\nevery hunk, not just the last one, to me.  Misnamed?\n\n> +\tfile=$1\n> +\tsed -n -e \"s/^.*@@$//p\" -e \"s/^.*@@ //p\" <$file\n\nStyle.\n\n - Redirection operators should be written with space before, but no\n   space after them.  In other words, write 'echo test >\"$file\"'\n   instead of 'echo test> $file' or 'echo test > $file'.  Note that\n   even though it is not required by POSIX to double-quote the\n   redirection target in a variable (as shown above), our code does so\n   because some versions of bash issue a warning without the quotes.\n\nIn any case, I wonder if this kind of clean-up should have been done\nimmediately before step 11/35, not after 11/35 started rewriting the\nframework.  Any touch-up done after 11/35 risks smelling like \"oops,\nwe found a better way to write it after we did the big rewrite\".\n\n> +}\n> +\n>  # check each individual file\n>  for i in $(git -C t4018 ls-files)\n>  do\n> @@ -85,13 +96,12 @@ do\n>  \n>  \t\t# add test file to the index\n>  \t\tgit add \\\"$i\\\" &&\n> -\t\t# place modified file in the worktree\n> -\t\tsed -e 's/ChangeMe/IWasChanged/' <\\\"t4018/$i.content\\\" >\\\"$i\\\"\n> +\t\tdo_change_me \\\"$i\\\"\n>  \t\"\n>  \n>  \ttest_expect_success \"hunk header: $i\" \"\n>  \t\tgit diff -U1 $i >diff &&\n> -\t\tsed -n -e 's/^.*@@$//p' -e 's/^.*@@ //p' <diff >ctx &&\n> +\t\tlast_diff_context_line diff >ctx &&\n>  \t\ttest_cmp t4018/$i.header ctx\n>  \t\"\n>  done\n"},{"id":"417830","messageId":"5fb2fd5c-7c69-a6fe-566d-227ed09946e4@kdbg.org","threadId":"55144","inReplyTo":"xmqqa6rsyi2u.fsf@gitster.g","subject":"Re: [PATCH v3 12/35] userdiff tests: change setup loop to individual test setup","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2021-02-25T18:28:35Z","receivedAt":"2021-02-25T18:29:35Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 25.02.21 um 03:52 schrieb Junio C Hamano:\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n> \n>> +# check each individual file\n>> +for i in $(git -C t4018 ls-files)\n>> +do\n>> +\ttest_expect_success \"setup hunk header: $i\" \"\n>> +\t\tgrep -v '^t4018' \\\"t4018/$i\\\" >\\\"t4018/$i.content\\\" &&\n>> +\t\tsed -n -e 's/^t4018 header: //p' <\\\"t4018/$i\\\" >\\\"t4018/$i.header\\\" &&\n>> +\t\tcp \\\"t4018/$i.content\\\" \\\"$i\\\" &&\n>>  \n>>  \t\t# add test file to the index\n>> -\t\tgit add \"$i\" &&\n>> +\t\tgit add \\\"$i\\\" &&\n>>  \t\t# place modified file in the worktree\n>> -\t\tsed -e \"s/ChangeMe/IWasChanged/\" <\"t4018/$i.content\" >\"$i\" || return 1\n>> -\tdone\n>> -'\n>> +\t\tsed -e 's/ChangeMe/IWasChanged/' <\\\"t4018/$i.content\\\" >\\\"$i\\\"\n>> +\t\"\n> \n> Please use '' around the second argument (i.e. test body) of the\n> test_expect_success, and use \"\" inside it.  \"$i\" that is used in the\n> loop is visible perfectly fine inside the test body when it is\n> eval'ed, and we won't have to count ugly backslashes that way.\n\nIf we do that, then we better be sure that the implementation of\ntest_expect_success does not clobber $i. Looks like we are OK at this time.\n\n-- Hannes\n"},{"id":"417836","messageId":"xmqqlfbcufub.fsf@gitster.g","threadId":"55144","inReplyTo":"5fb2fd5c-7c69-a6fe-566d-227ed09946e4@kdbg.org","subject":"Re: [PATCH v3 12/35] userdiff tests: change setup loop to individual test setup","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-02-25T19:06:52Z","receivedAt":"2021-02-25T19:08:39Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Sixt <j6t@kdbg.org> writes:\n\n> Am 25.02.21 um 03:52 schrieb Junio C Hamano:\n>> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>> \n>>> +# check each individual file\n>>> +for i in $(git -C t4018 ls-files)\n>>> +do\n>>> +\ttest_expect_success \"setup hunk header: $i\" \"\n>>> +\t\tgrep -v '^t4018' \\\"t4018/$i\\\" >\\\"t4018/$i.content\\\" &&\n>>> +\t\tsed -n -e 's/^t4018 header: //p' <\\\"t4018/$i\\\" >\\\"t4018/$i.header\\\" &&\n>>> +\t\tcp \\\"t4018/$i.content\\\" \\\"$i\\\" &&\n>>>  \n>>>  \t\t# add test file to the index\n>>> -\t\tgit add \"$i\" &&\n>>> +\t\tgit add \\\"$i\\\" &&\n>>>  \t\t# place modified file in the worktree\n>>> -\t\tsed -e \"s/ChangeMe/IWasChanged/\" <\"t4018/$i.content\" >\"$i\" || return 1\n>>> -\tdone\n>>> -'\n>>> +\t\tsed -e 's/ChangeMe/IWasChanged/' <\\\"t4018/$i.content\\\" >\\\"$i\\\"\n>>> +\t\"\n>> \n>> Please use '' around the second argument (i.e. test body) of the\n>> test_expect_success, and use \"\" inside it.  \"$i\" that is used in the\n>> loop is visible perfectly fine inside the test body when it is\n>> eval'ed, and we won't have to count ugly backslashes that way.\n>\n> If we do that, then we better be sure that the implementation of\n> test_expect_success does not clobber $i. Looks like we are OK at this time.\n\nThat's a good point.  Often we use more specific variable name than\n$i to take advantage of the 'eval'-ed nature of the test body, and\nit may probably be wise to do so here, too.\n"},{"id":"417883","messageId":"fb6b3681-314c-eda9-aab3-2a85ebca92ca@kdbg.org","threadId":"55144","inReplyTo":"20210224195129.4004-32-avarab@gmail.com","subject":"Re: [PATCH v3 31/35] userdiff golang: don't over-match intented constructs","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2021-02-26T07:59:00Z","receivedAt":"2021-02-26T07:59:38Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 24.02.21 um 20:51 schrieb Ævar Arnfjörð Bjarmason:\n> Fix a bug introduced when the \"golang\" driver was added in\n> 1dbf0c0ad6c (userdiff: add built-in pattern for golang, 2018-03-01).\n> \n> Unlike the default def_ff() driver in xemit.c it would match \"type\"\n> declarations inside functions. Let's make it mandatory that a \"func\"\n> or \"type\" must be at the beginning of the line with no whitespace to\n> get around this.\n> \n> Go is such a regularly formatted language that I think this can be\n> counted on. I think the whitespace matching was probably copy/pasted\n> from an earlier userdiff.c pattern.\n> \n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  t/t4018/golang | 20 ++++++++++++++++++++\n>  userdiff.c     |  4 ++--\n>  2 files changed, 22 insertions(+), 2 deletions(-)\n> \n> diff --git a/t/t4018/golang b/t/t4018/golang\n> index 72a35d66008..252b6049da4 100644\n> --- a/t/t4018/golang\n> +++ b/t/t4018/golang\n> @@ -51,3 +51,23 @@ type some struct { // comment\n>  \ta Type\n>  \tb ChangeMe\n>  }\n> +\n> +t4018 description: func combined with type\n> +t4018 header: func myfunc() {\n> +func myfunc() {\n> +\ttype mystruct struct {\n> +\t\ta Foo\n> +\t\tb Bar\n> +\t}\n> +\tChangeMe\n> +\n> +t4018 description: anonymous indented func()\n> +t4018 header: func SomeThing() bool {\n> +func SomeThing() bool {\n> +\tfunc() {\n\nWait! With the unmodified pattern, this indented \"func()\" cannot match\nbecause it is not followed by a space. So, this test case does not\ndemonstrate that the modified pattern makes a difference. In the commit\nmessage of 30/35 you say that \"we should always get whitespace after\n'func'\", but here you show a case where that is not true. Did you forget\nto write a name after \"func\"?\n\n> +\t\tdefer func() {\n> +\t\t\tfmt.Println(\"hello\")\n> +\t\t}()\n> +\t}()\n> +\n> +\tChangeMe\n> diff --git a/userdiff.c b/userdiff.c\n> index 698eca5ad35..704af241e44 100644\n> --- a/userdiff.c\n> +++ b/userdiff.c\n> @@ -129,9 +129,9 @@ IPATTERN(\"fountain\",\n>  \t \"[^ \\t-]+\"),\n>  PATTERNS(\"golang\",\n>  \t /* Functions */\n> -\t \"^[ \\t]*(func[ \\t].*)\\n\"\n> +\t \"^(func[ \\t].*)\\n\"\n>  \t /* Structs and interfaces */\n> -\t \"^[ \\t]*(type[ \\t].*(struct|interface)[ \\t].*)\",\n> +\t \"^(type[ \\t].*[ \\t](struct|interface)[ \\t].*)\",\n>  \t /* -- */\n>  \t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n>  \t \"|[-+0-9.eE]+i?|0[xX]?[0-9a-fA-F]+i?\"\n> \n\n"},{"id":"417885","messageId":"6905d873-2cba-638b-bbfe-e652a16cd65c@kdbg.org","threadId":"55144","inReplyTo":"20210224195129.4004-33-avarab@gmail.com","subject":"Re: [PATCH v3 32/35] userdiff golang: add a rule to match \"package\"","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2021-02-26T08:03:45Z","receivedAt":"2021-02-26T08:04:35Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 24.02.21 um 20:51 schrieb Ævar Arnfjörð Bjarmason:\n> Improve the \"golang\" built-in pattern to match \"package\" lines, as\n> they weren't matched before changing e.g. the imports would commonly\n> result in an empty hunk header, now we'll instead show the package\n> name.\n> \n> I used https://blog.golang.org/package-names as a guide here, but\n> e.g. \"foo_bar\" is still valid syntax, so let's let it pass but veer on\n> the side of not having false positives.\n> \n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  t/t4018/golang | 49 +++++++++++++++++++++++++++++++++++++++++++++++++\n>  userdiff.c     |  2 ++\n>  2 files changed, 51 insertions(+)\n> \n> diff --git a/t/t4018/golang b/t/t4018/golang\n> index 252b6049da4..38f254cd269 100644\n> --- a/t/t4018/golang\n> +++ b/t/t4018/golang\n> @@ -1,3 +1,52 @@\n> +t4018 description: package\n> +t4018 header: package main\n> +package main\n> +\n> +import \"fmt\"\n> +// ChangeMe\n> +\n> +t4018 description: package regex is selective -- ALLCAPS\n> +t4018 header: package main\n> +package ALLCAPS\n> +\n> +import \"fmt\"\n> +// ChangeMe\n> +\n> +t4018 description: package regex is selective -- CamelCase\n> +t4018 header: package main\n> +package CamelCase\n> +\n> +import \"fmt\"\n> +// ChangeMe\n> +\n> +t4018 description: package regex is selective -- 123\n> +t4018 header: package main\n> +package 123\n> +\n> +import \"fmt\"\n> +// ChangeMe\n> +\n> +t4018 description: package regex is not overly selective -- x509\n> +t4018 header: package x509\n> +package x509\n> +\n> +import \"fmt\"\n> +// ChangeMe\n> +\n> +t4018 description: package regex is not overly selective -- underbars\n> +t4018 header: package not_conventional\n> +package not_conventional\n> +\n> +import \"fmt\"\n> +// ChangeMe\n> +\n> +t4018 description: package regex is not overly selective -- camelCase\n> +t4018 header: package camelCase\n> +package camelCase\n> +\n> +import \"fmt\"\n> +// ChangeMe\n> +\n>  t4018 description: complex function\n>  t4018 header: func (t *Test) RIGHT(a Type) (Type, error) {\n>  type Test struct {\n> diff --git a/userdiff.c b/userdiff.c\n> index 704af241e44..bbbbfa33e0a 100644\n> --- a/userdiff.c\n> +++ b/userdiff.c\n> @@ -128,6 +128,8 @@ IPATTERN(\"fountain\",\n>  \t /* -- */\n>  \t \"[^ \\t-]+\"),\n>  PATTERNS(\"golang\",\n> +\t /* Packages */\n> +\t \"^(package[ \\t][a-z][A-Za-z0-9_]+)[ \\t]*\\n\"\n\nJust a single whitespace character is permitted between the keyword and\nthe name?\n\n>  \t /* Functions */\n>  \t \"^(func[ \\t].*)\\n\"\n>  \t /* Structs and interfaces */\n> \n\n"},{"id":"417939","messageId":"a756d24e-53b9-0245-de68-be96946ba5f9@kdbg.org","threadId":"55144","inReplyTo":"20210224195129.4004-35-avarab@gmail.com","subject":"Re: [PATCH v3 34/35] userdiff tests: add basic test for ada","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2021-02-27T07:26:18Z","receivedAt":"2021-02-27T07:26:45Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 24.02.21 um 20:51 schrieb Ævar Arnfjörð Bjarmason:\n> Add test for the ada userdiff pattern added in e90d065e64 (Add\n> userdiff patterns for Ada, 2012-09-16).\n> \n> I don't know the ada language itself, I just stole a couple of\n> examples of code that used tokens we're matching[1][2]. Both test\n> examples stress our negative and positive matching rules.\n> \n> 1. https://rosettacode.org/wiki/99_bottles_of_beer#Ada\n> 2. https://en.wikibooks.org/wiki/Ada_Programming/Tasking\n\nMissing Signed-off-by.\n\n> ---\n>  t/t4018-diff-funcname.sh |  1 -\n>  t/t4018/ada.sh           | 37 +++++++++++++++++++++++++++++++++++++\n>  2 files changed, 37 insertions(+), 1 deletion(-)\n>  create mode 100755 t/t4018/ada.sh\n> \n> diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\n> index ba10d1f5313..b0c2782d067 100755\n> --- a/t/t4018-diff-funcname.sh\n> +++ b/t/t4018-diff-funcname.sh\n> @@ -18,7 +18,6 @@ test_expect_success 'setup' '\n>  \t# Do not add anything to this list. New built-in drivers should have\n>  \t# tests\n>  \tcat >drivers-no-tests <<-\\EOF &&\n> -\tada\n>  \tbibtex\n>  \tcsharp\n>  \thtml\n> diff --git a/t/t4018/ada.sh b/t/t4018/ada.sh\n> new file mode 100755\n> index 00000000000..45fc2c7a3b2\n> --- /dev/null\n> +++ b/t/t4018/ada.sh\n> @@ -0,0 +1,37 @@\n> +#!/bin/sh\n> +#\n> +# See ../t4018-diff-funcname.sh's test_diff_funcname()\n> +#\n> +\n> +test_diff_funcname 'ada: \"procedure\" over \"with\"' \\\n> +\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n> +procedure Bottles is\n> +EOF_HUNK\n> +with Ada.Text_Io; use Ada.Text_Io;\n> + procedure Bottles is\n> + begin\n> +    for X in reverse 1..99 loop\n> +       Put_Line(Integer'Image(X) & \" bottles of beer on the wall\");\n> +       Put_Line(Integer'Image(X) & \" bottles of beer\"); -- ChangeMe\n> +       Put_Line(\"Take one down, pass it around\");\n> +       Put_Line(Integer'Image(X - 1) & \" bottles of beer on the wall\");\n> +       New_Line;\n> +    end loop;\n> + end Bottles;\n> +EOF_TEST\n> +\n> +test_diff_funcname 'ada: \"task\" over \"procedure\"' \\\n> +\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n> +task body Check_CPU is\n> +EOF_HUNK\n> +procedure Housekeeping is\n> +  task Check_CPU;\n> +  task Backup_Disk;\n> +\n> +  task body Check_CPU is\n> +    -- Comment for spacing with\n> +    -- the above \"task\" for -U1\n\nAren't we using -U0 by now?\n\n> +    ChangeMe\n> +  end Check_CPU;\n> +end Housekeeping;\n> +EOF_TEST\n> \n\nI don't see anything special in these tests. Couldn't you just use the\nsimplified version of tests, which are much easier to read than this\nshell script with EOF markers?\n\n-- Hannes\n"},{"id":"417940","messageId":"6d4bde46-029b-b36b-a58a-a877a4a3d14c@kdbg.org","threadId":"55144","inReplyTo":"20210224195129.4004-36-avarab@gmail.com","subject":"Re: [PATCH v3 35/35] userdiff tests: add basic test for ruby","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2021-02-27T07:30:08Z","receivedAt":"2021-02-27T07:30:31Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 24.02.21 um 20:51 schrieb Ævar Arnfjörð Bjarmason:\n> Add a test for the Ruby pattern added way back in ad8c1d9260 (diff:\n> add ruby funcname pattern, 2008-07-31).\n> \n> The \"One/Two\" picking demonstrates existing behavior, and a general\n> case where we may not do what the user expects since we're not aware\n> of the indentation level.\n> \n> The code is modified from the Ruby code we have in-tree at\n> Documentation/asciidoctor-extensions.rb.\n> \n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  t/t4018-diff-funcname.sh |  1 -\n>  t/t4018/ruby.sh          | 58 ++++++++++++++++++++++++++++++++++++++++\n>  2 files changed, 58 insertions(+), 1 deletion(-)\n>  create mode 100755 t/t4018/ruby.sh\n> \n> diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\n> index b0c2782d067..7793d7652d5 100755\n> --- a/t/t4018-diff-funcname.sh\n> +++ b/t/t4018-diff-funcname.sh\n> @@ -23,7 +23,6 @@ test_expect_success 'setup' '\n>  \thtml\n>  \tobjc\n>  \tpascal\n> -\truby\n>  \ttex\n>  \tEOF\n>  \n> diff --git a/t/t4018/ruby.sh b/t/t4018/ruby.sh\n> new file mode 100755\n> index 00000000000..ef8a154421a\n> --- /dev/null\n> +++ b/t/t4018/ruby.sh\n> @@ -0,0 +1,58 @@\n> +#!/bin/sh\n> +#\n> +# See ../t4018-diff-funcname.sh's test_diff_funcname()\n> +#\n> +\n> +test_diff_funcname 'ruby: \"def\" over \"class/module\"' \\\n> +\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n> +def process(parent)\n> +EOF_HUNK\n> +require 'asciidoctor'\n> +\n> +module Git\n> +  module Documentation\n> +    class SomeClass\n> +      use_some\n> +\n> +      def process(parent)\n> +        puts(\"hello\")\n> +\tputs(ChangeMe)\n> +      end\n> +    end\n> +  end\n> +end\n> +EOF_TEST\n> +\n> +test_diff_funcname 'ruby: \"class\" over \"class/module\"' \\\n> +\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n> +class Two\n> +EOF_HUNK\n> +module Git\n> +  module Documentation\n> +    class One\n> +    end\n> +\n> +    class Two\n> +      # Spacing for -U1\n\nAgain: -U0 is the default by now.\n\n> +      ChangeMe\n> +    end\n> +  end\n> +end\n> +EOF_TEST\n> +\n> +test_diff_funcname 'ruby: picks first \"class/module/def\" before changed context' \\\n> +\t'-U1' \\\n> +\t8<<\\EOF_HUNK 9<<\\EOF_TEST\n> +class One\n> +EOF_HUNK\n> +module Git\n> +  module Documentation\n> +    class One\n> +    end\n> +\n> +    class Two\n> +      ChangeMe\n> +    end\n> +  end\n> +end\n> +EOF_TEST\n\nWhy does this test need -U1? Wouldn't it be sufficient to change the\nexpected text to \"class Two\"? And if the answer is \"yes\", I again do not\nsee the need for a shell script test here; the simplified version would do.\n\n-- Hannes\n"},{"id":"417942","messageId":"c2e90134-35bb-24b2-a334-7c6abdd6dc6f@kdbg.org","threadId":"55144","inReplyTo":"20210224195129.4004-1-avarab@gmail.com","subject":"Re: [PATCH v3 00/35] 20210215154427.32693-1-avarab@gmail.com","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2021-02-27T07:47:09Z","receivedAt":"2021-02-27T07:47:49Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 24.02.21 um 20:50 schrieb Ævar Arnfjörð Bjarmason:\n> Addresses feedback on v2. Since Junio & Johannes expressed a desire to\n> keep the existing test scheme in t4018/* it's still there, but it's\n> also possible to add *.sh tests in that directory to use the more\n> familiar test framework used elsewhere in the test suite.\n> \n> The tests added here make use of it to e.g. supply custom -U<n>\n> arguments, set config before the tests etc.\n> \n> I also improved that existing test support so you can have N tests in\n> one file with (mostly) the existing test syntax. See the \"userdiff\n> tests: add a test with multiple tests in a LANG file\" patch.\n\nI've read through all patches and had a comment here and there. I like a\nlot that we can now put more than one test into a single file.\n\nHowever, I do not like the shell script version of tests, because the\nsyntax is so hard to read. Also, it looks to me that they are only\nneeded for a few tests that could just as well be coded as one-off tests\noutside the framework.\n\nI've now pulled avar/t4018-diff-hunk-header-regex-tests-3 from your\ngithub repo and will check again if I missed some cruicial points.\n\n-- Hannes\n"},{"id":"417964","messageId":"377d555c-6bd0-4206-3f71-d4d51aaca692@kdbg.org","threadId":"55144","inReplyTo":"20210224195129.4004-21-avarab@gmail.com","subject":"Re: [PATCH v3 20/35] userdiff tests: assert that new built-in drivers have tests","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2021-02-28T10:34:09Z","receivedAt":"2021-02-28T10:34:52Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 24.02.21 um 20:51 schrieb Ævar Arnfjörð Bjarmason:\n> Add an assertion to the userdiff test framework to check that\n> everything except a narrow whitelist of existing built-in patterns has\n> tests.\n> \n> Since this test framework was added we've added new patterns without\n> any tests. Let's make it obvious in the future in the diff for such\n> patches that they should have those tests.\n> \n> For anything with tests we can skip the \"does the pattern compile?\"\n> test, as the actual tests will check that for us.\n> \n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  t/t4018-diff-funcname.sh | 30 +++++++++++++++++++++++++++++-\n>  1 file changed, 29 insertions(+), 1 deletion(-)\n> \n> diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\n> index b80546b4d7f..a3058fda130 100755\n> --- a/t/t4018-diff-funcname.sh\n> +++ b/t/t4018-diff-funcname.sh\n> @@ -15,6 +15,19 @@ test_expect_success 'setup' '\n>  \tsort <builtin-drivers >builtin-drivers.sorted &&\n>  \ttest_cmp builtin-drivers.sorted builtin-drivers &&\n>  \n> +\t# Do not add anything to this list. New built-in drivers should have\n> +\t# tests\n> +\tcat >drivers-no-tests <<-\\EOF &&\n> +\tada\n> +\tbibtex\n> +\tcsharp\n> +\thtml\n> +\tobjc\n> +\tpascal\n> +\truby\n> +\ttex\n> +\tEOF\n> +\n>  \t# for regexp compilation tests\n>  \techo A >A.java &&\n>  \techo B >B.java\n> @@ -22,7 +35,12 @@ test_expect_success 'setup' '\n>  \n>  for p in $(cat builtin-drivers)\n>  do\n> -\ttest_expect_success \"builtin $p pattern compiles\" '\n> +\tP=$(echo $p | tr 'a-z' 'A-Z')\n> +\tif grep -q $p drivers-no-tests\n> +\tthen\n> +\t\ttest_set_prereq NO_TEST_FOR_DRIVER_$P\n> +\tfi\n> +\ttest_expect_success NO_TEST_FOR_DRIVER_$P \"builtin $p pattern compiles\" '\n>  \t\techo \"*.java diff=$p\" >.gitattributes &&\n>  \t\ttest_expect_code 1 git diff --no-index \\\n>  \t\t\tA.java B.java 2>msg &&\n\nPlease drop this hunk. It is extremly distracting to see, e.g.,\n\nok 8 # skip builtin cpp pattern compiles (missing NO_TEST_FOR_DRIVER_CPP)\nok 9 - builtin cpp wordRegex pattern compiles\n\nIt says \"NO_TEST_FOR_DRIVER_CPP\", but I know we have tests. I have to\nwaste time to check that something not related to \"we have tests for the\ndriver\" is meant here. It may be just a matter of naming the\nprerequisite, but I think we do not need this optimization.\n\n> @@ -119,11 +137,17 @@ test_diff_funcname () {\n>  \t'\n>  }\n>  \n> +>drivers-had-no-tests\n>  for what in $diffpatterns\n>  do\n>  \ttest=\"$TEST_DIRECTORY/t4018/$what.sh\"\n>  \tif ! test -e \"$test\"\n>  \tthen\n> +\t\tgit -C t4018 ls-files ':!*.sh' \"$what*\" >other-tests &&\n> +\t\tif ! test -s other-tests\n> +\t\tthen\n> +\t\t\techo $what >>drivers-had-no-tests\n> +\t\tfi\n>  \t\tcontinue\n>  \tfi &&\n>  \n> @@ -135,4 +159,8 @@ do\n>  \t. \"$test\"\n>  done\n>  \n> +test_expect_success 'we should not have new built-in drivers without tests' '\n> +\ttest_cmp drivers-no-tests drivers-had-no-tests\n> +'\n> +\n>  test_done\n> \n\n"},{"id":"417966","messageId":"3519e86e-d9eb-a9e0-ed28-72d32271d2bf@kdbg.org","threadId":"55144","inReplyTo":"c2e90134-35bb-24b2-a334-7c6abdd6dc6f@kdbg.org","subject":"Re: [PATCH v3 00/35] 20210215154427.32693-1-avarab@gmail.com","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2021-02-28T11:04:57Z","receivedAt":"2021-02-28T11:05:37Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 27.02.21 um 08:47 schrieb Johannes Sixt:\n> Am 24.02.21 um 20:50 schrieb Ævar Arnfjörð Bjarmason:\n>> Addresses feedback on v2. Since Junio & Johannes expressed a desire to\n>> keep the existing test scheme in t4018/* it's still there, but it's\n>> also possible to add *.sh tests in that directory to use the more\n>> familiar test framework used elsewhere in the test suite.\n>>\n>> The tests added here make use of it to e.g. supply custom -U<n>\n>> arguments, set config before the tests etc.\n>>\n>> I also improved that existing test support so you can have N tests in\n>> one file with (mostly) the existing test syntax. See the \"userdiff\n>> tests: add a test with multiple tests in a LANG file\" patch.\n> \n> I've read through all patches and had a comment here and there. I like a\n> lot that we can now put more than one test into a single file.\n> \n> However, I do not like the shell script version of tests, because the\n> syntax is so hard to read. Also, it looks to me that they are only\n> needed for a few tests that could just as well be coded as one-off tests\n> outside the framework.\n> \n> I've now pulled avar/t4018-diff-hunk-header-regex-tests-3 from your\n> github repo and will check again if I missed some cruicial points.\n\n\nI've now looked through the patch series again.\n\nI appreciate that you dug through the history and discovered and fixed a\nfew deficiencies and loose ends. The way to throw all test cases for a\nlanguage driver into a single file I like a lot, too. The way to specify\nexpected results is manageable (modulo the dependency on the test\nnumber, t4018, that Junio mentioned).\n\nBut I do not see the need for the framework provided by the new\ntest_diff_funcname. At the end of the series, it is only used by Perl\nand custom driver tests. (I discount the new ADA and Ruby tests as they\ncan easily migrated to the simple test scheme.) But then the Perl tests\ndo not do anything special, either. The multi-line pattern test is just\na nice add-on but not strictly needed. In the end, the Perl test is just\nas straight-forward as all others.\n\nThat leaves the custom driver tests. I don't see the need for a new\nframework just for that. It would be sufficient to integrate them in\nmain test file t4018-diff-funcname.sh if needed, or leave them in the\nexisting framework if possible.\n\n-- Hannes\n"},{"id":"417967","messageId":"d3ee44f3-e7d7-e9a9-8fde-1bd48323c150@kdbg.org","threadId":"55144","inReplyTo":"20210224195129.4004-15-avarab@gmail.com","subject":"Re: [PATCH v3 14/35] userdiff tests: add alternative hunk header test infrastructure","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2021-02-28T11:12:59Z","receivedAt":"2021-02-28T11:13:47Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 24.02.21 um 20:51 schrieb Ævar Arnfjörð Bjarmason:\n> diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\n> index 8a8a7a99c88..6fd3dce1364 100755\n> --- a/t/t4018-diff-funcname.sh\n> +++ b/t/t4018-diff-funcname.sh\n> @@ -87,7 +87,7 @@ last_diff_context_line () {\n>  }\n>  \n>  # check each individual file\n> -for i in $(git -C t4018 ls-files)\n> +for i in $(git -C t4018 ls-files -- ':!*.sh')\n>  do\n>  \ttest_expect_success \"setup hunk header: $i\" \"\n>  \t\tgrep -v '^t4018' \\\"t4018/$i\\\" >\\\"t4018/$i.content\\\" &&\n> @@ -106,4 +106,40 @@ do\n>  \t\"\n>  done\n>  \n> +test_diff_funcname () {\n> +\tdesc=$1\n> +\tcat <&8 >arg.header &&\n> +\tcat <&9 >arg.test &&\n> +\twhat=$(cat arg.what) &&\n> +\n> +\ttest_expect_success \"setup: $desc\" '\n> +\t\tcp arg.test \"$what\" &&\n> +\t\tcp arg.header expected &&\n> +\t\tgit add \"$what\" &&\n> +\t\tdo_change_me \"$what\"\n> +\t' &&\n\nI think we should not chain test_expect_success with && because when one\nof the tests early in the chain fails, test case numbers are shifted\ncompared to when all tests succeed. I don't know, though, whether that\nis an important thing (or a thing at all).\n\n> +\n> +\ttest_expect_success \"$desc\" '\n> +\t\tgit diff -U1 \"$what\" >diff &&\n> +\t\tlast_diff_context_line diff >actual &&\n> +\t\ttest_cmp expected actual\n> +\t'\n> +}\n> +\n> +for what in $diffpatterns\n> +do\n> +\ttest=\"$TEST_DIRECTORY/t4018/$what.sh\"\n> +\tif ! test -e \"$test\"\n> +\tthen\n> +\t\tcontinue\n> +\tfi &&\n> +\n> +\ttest_expect_success \"setup: hunk header for $what\" '\n> +\t\techo \"$what diff=$what\" >.gitattributes &&\n> +\t\techo \"$what\" >arg.what\n> +\t' &&\n\nSame here. And when this is removed, the && just above is not needed\nanymore, either.\n\n> +\n> +\t. \"$test\"\n> +done\n> +\n>  test_done\n\n-- Hannes\n"},{"id":"417977","messageId":"878s78kx5s.fsf@evledraar.gmail.com","threadId":"55144","inReplyTo":"377d555c-6bd0-4206-3f71-d4d51aaca692@kdbg.org","subject":"Re: [PATCH v3 20/35] userdiff tests: assert that new built-in drivers have tests","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-28T15:51:59Z","receivedAt":"2021-02-28T15:52:52Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Sun, Feb 28 2021, Johannes Sixt wrote:\n\n> Am 24.02.21 um 20:51 schrieb Ævar Arnfjörð Bjarmason:\n>> Add an assertion to the userdiff test framework to check that\n>> everything except a narrow whitelist of existing built-in patterns has\n>> tests.\n>> \n>> Since this test framework was added we've added new patterns without\n>> any tests. Let's make it obvious in the future in the diff for such\n>> patches that they should have those tests.\n>> \n>> For anything with tests we can skip the \"does the pattern compile?\"\n>> test, as the actual tests will check that for us.\n>> \n>> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>> ---\n>>  t/t4018-diff-funcname.sh | 30 +++++++++++++++++++++++++++++-\n>>  1 file changed, 29 insertions(+), 1 deletion(-)\n>> \n>> diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\n>> index b80546b4d7f..a3058fda130 100755\n>> --- a/t/t4018-diff-funcname.sh\n>> +++ b/t/t4018-diff-funcname.sh\n>> @@ -15,6 +15,19 @@ test_expect_success 'setup' '\n>>  \tsort <builtin-drivers >builtin-drivers.sorted &&\n>>  \ttest_cmp builtin-drivers.sorted builtin-drivers &&\n>>  \n>> +\t# Do not add anything to this list. New built-in drivers should have\n>> +\t# tests\n>> +\tcat >drivers-no-tests <<-\\EOF &&\n>> +\tada\n>> +\tbibtex\n>> +\tcsharp\n>> +\thtml\n>> +\tobjc\n>> +\tpascal\n>> +\truby\n>> +\ttex\n>> +\tEOF\n>> +\n>>  \t# for regexp compilation tests\n>>  \techo A >A.java &&\n>>  \techo B >B.java\n>> @@ -22,7 +35,12 @@ test_expect_success 'setup' '\n>>  \n>>  for p in $(cat builtin-drivers)\n>>  do\n>> -\ttest_expect_success \"builtin $p pattern compiles\" '\n>> +\tP=$(echo $p | tr 'a-z' 'A-Z')\n>> +\tif grep -q $p drivers-no-tests\n>> +\tthen\n>> +\t\ttest_set_prereq NO_TEST_FOR_DRIVER_$P\n>> +\tfi\n>> +\ttest_expect_success NO_TEST_FOR_DRIVER_$P \"builtin $p pattern compiles\" '\n>>  \t\techo \"*.java diff=$p\" >.gitattributes &&\n>>  \t\ttest_expect_code 1 git diff --no-index \\\n>>  \t\t\tA.java B.java 2>msg &&\n>\n> Please drop this hunk. It is extremly distracting to see, e.g.,\n>\n> ok 8 # skip builtin cpp pattern compiles (missing NO_TEST_FOR_DRIVER_CPP)\n> ok 9 - builtin cpp wordRegex pattern compiles\n>\n> It says \"NO_TEST_FOR_DRIVER_CPP\", but I know we have tests. I have to\n> waste time to check that something not related to \"we have tests for the\n> driver\" is meant here. It may be just a matter of naming the\n> prerequisite, but I think we do not need this optimization.\n\nPerhaps some other way to do this is better. I did the prerequisite just\nto avoid indenting that whole part and I figured it was more readable,\nbut apparently not on the \"more readable\".\n\nI do think it makes sense to keep this in some form, i.e. not test the\ncompilation if we know we have later tests to compile the regex. It's\nproviding self-documenting code to show that once we add tests for the\nfew missing drivers we can delete that test entirely.\n\nAnd if a later change does the same check on the t4034/* files the\n--word-diff check can also go.\n\n>> @@ -119,11 +137,17 @@ test_diff_funcname () {\n>>  \t'\n>>  }\n>>  \n>> +>drivers-had-no-tests\n>>  for what in $diffpatterns\n>>  do\n>>  \ttest=\"$TEST_DIRECTORY/t4018/$what.sh\"\n>>  \tif ! test -e \"$test\"\n>>  \tthen\n>> +\t\tgit -C t4018 ls-files ':!*.sh' \"$what*\" >other-tests &&\n>> +\t\tif ! test -s other-tests\n>> +\t\tthen\n>> +\t\t\techo $what >>drivers-had-no-tests\n>> +\t\tfi\n>>  \t\tcontinue\n>>  \tfi &&\n>>  \n>> @@ -135,4 +159,8 @@ do\n>>  \t. \"$test\"\n>>  done\n>>  \n>> +test_expect_success 'we should not have new built-in drivers without tests' '\n>> +\ttest_cmp drivers-no-tests drivers-had-no-tests\n>> +'\n>> +\n>>  test_done\n>> \n\n"},{"id":"417978","messageId":"875z2ckwfl.fsf@evledraar.gmail.com","threadId":"55144","inReplyTo":"3519e86e-d9eb-a9e0-ed28-72d32271d2bf@kdbg.org","subject":"Re: [PATCH v3 00/35] 20210215154427.32693-1-avarab@gmail.com","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-28T16:07:42Z","receivedAt":"2021-02-28T16:08:42Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Sun, Feb 28 2021, Johannes Sixt wrote:\n\n> Am 27.02.21 um 08:47 schrieb Johannes Sixt:\n>> Am 24.02.21 um 20:50 schrieb Ævar Arnfjörð Bjarmason:\n>>> Addresses feedback on v2. Since Junio & Johannes expressed a desire to\n>>> keep the existing test scheme in t4018/* it's still there, but it's\n>>> also possible to add *.sh tests in that directory to use the more\n>>> familiar test framework used elsewhere in the test suite.\n>>>\n>>> The tests added here make use of it to e.g. supply custom -U<n>\n>>> arguments, set config before the tests etc.\n>>>\n>>> I also improved that existing test support so you can have N tests in\n>>> one file with (mostly) the existing test syntax. See the \"userdiff\n>>> tests: add a test with multiple tests in a LANG file\" patch.\n>> \n>> I've read through all patches and had a comment here and there. I like a\n>> lot that we can now put more than one test into a single file.\n>> \n>> However, I do not like the shell script version of tests, because the\n>> syntax is so hard to read. Also, it looks to me that they are only\n>> needed for a few tests that could just as well be coded as one-off tests\n>> outside the framework.\n>> \n>> I've now pulled avar/t4018-diff-hunk-header-regex-tests-3 from your\n>> github repo and will check again if I missed some cruicial points.\n>\n>\n> I've now looked through the patch series again.\n>\n> I appreciate that you dug through the history and discovered and fixed a\n> few deficiencies and loose ends. The way to throw all test cases for a\n> language driver into a single file I like a lot, too. The way to specify\n> expected results is manageable (modulo the dependency on the test\n> number, t4018, that Junio mentioned).\n\nYes, maybe just \"HEADER:\" or something would be better. I was trying to\ncome up with something guaranteed not to conflict with the language in\nquestion. Maybe:\n\n    => HEADER      = \n    => description = \n\n> But I do not see the need for the framework provided by the new\n> test_diff_funcname. At the end of the series, it is only used by Perl\n> and custom driver tests. (I discount the new ADA and Ruby tests as they\n> can easily migrated to the simple test scheme.) But then the Perl tests\n> do not do anything special, either. The multi-line pattern test is just\n> a nice add-on but not strictly needed. In the end, the Perl test is just\n> as straight-forward as all others.\n\nThe benefit now is:\n\n 1. Unlike the new plain-text \"all test cases for a language driver\" in\n    the same file mechanism you can have test descriptions. The\n    \"description\" in the golang one is just for show, you won't get\n    anything informative from test-lib.sh when your test fails.\n\n 2. I think having #1 beats not having test descriptions at all, or\n     having to shove a description like the Ruby:\n\n    \"picks first \"class/module/def\" before changed context\"\n\n    into something that would make all the FS's we have to support\n    happy.\n\n 3. A test in the new perl.sh one sets config. I think in both that case\n    and custom.sh it's more readable to carry such config setting with\n    the test, rather than at a distance in the main setup of t4018-*.sh.\n\nThat being said I'd like to improve the syntax a bit, in particular\ninstead of having a wrapper for test_expect_success I think it makes\nsense just to have the test call test_expect_success, and then provide a\ncouple of helper functions.\n"},{"id":"418003","messageId":"b9a0406d-a913-767c-fbec-be57246bd4e4@kdbg.org","threadId":"55144","inReplyTo":"875z2ckwfl.fsf@evledraar.gmail.com","subject":"Re: [PATCH v3 00/35] 20210215154427.32693-1-avarab@gmail.com","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2021-03-01T07:10:16Z","receivedAt":"2021-03-01T07:11:20Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 28.02.21 um 17:07 schrieb Ævar Arnfjörð Bjarmason:\n> On Sun, Feb 28 2021, Johannes Sixt wrote:\n>> But I do not see the need for the framework provided by the new\n>> test_diff_funcname. At the end of the series, it is only used by Perl\n>> and custom driver tests. (I discount the new ADA and Ruby tests as they\n>> can easily migrated to the simple test scheme.) But then the Perl tests\n>> do not do anything special, either. The multi-line pattern test is just\n>> a nice add-on but not strictly needed. In the end, the Perl test is just\n>> as straight-forward as all others.\n> \n> The benefit now is:\n> \n>  1. Unlike the new plain-text \"all test cases for a language driver\" in\n>     the same file mechanism you can have test descriptions. The\n>     \"description\" in the golang one is just for show, you won't get\n>     anything informative from test-lib.sh when your test fails.\n> \n>  2. I think having #1 beats not having test descriptions at all, or\n>      having to shove a description like the Ruby:\n> \n>     \"picks first \"class/module/def\" before changed context\"\n> \n>     into something that would make all the FS's we have to support\n>     happy.\n\nI missed that the descriptions are gone now that many test cases are\nshoved into a single file when the simple framework is used. That is\nindeed a disadvantage. But please keep in mind that code is more often\nread than written. *If* we have to grow a new framework, then it must\nnot suffer from unreadability.\n\n> \n>  3. A test in the new perl.sh one sets config. I think in both that case\n>     and custom.sh it's more readable to carry such config setting with\n>     the test, rather than at a distance in the main setup of t4018-*.sh.\n\nAs I said, the config in perl.sh is only used for a multi-line pattern\ntest. That is dispensable as there is already a similar test with custom\npatterns. And the tests for custom patterns can be moved to the main\ntest file entirely.\n\n> That being said I'd like to improve the syntax a bit, in particular\n> instead of having a wrapper for test_expect_success I think it makes\n> sense just to have the test call test_expect_success, and then provide a\n> couple of helper functions.\n\nThat sounds like an improvement.\n\n-- Hannes\n"},{"id":"420085","messageId":"patch-01.11-fb7346cd296-20210324T014604Z-avarab@gmail.com","threadId":"55144","inReplyTo":"cover-00.11-00000000000-20210324T014604Z-avarab@gmail.com","subject":"[PATCH v4 01/10] userdiff: refactor away the parse_bool() function","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-24T01:48:43Z","receivedAt":"2021-03-24T01:50:04Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Since 6680a0874f (drop odd return value semantics from\nuserdiff_config, 2012-02-07) we have not cared about the return values\nof parse_tristate() or git_config_bool() v.s. falling through in\nuserdiff_config(), so let's do so in those cases to make the code\neasier to read.\n\nHaving a wrapper function for git_config_bool() dates back to\nd9bae1a178 (diff: cache textconv output, 2010-04-01) and\n122aa6f9c0 (diff: introduce diff.<driver>.binary, 2008-10-05), both of\nwhich predated the change in 6680a0874f which made their return values\nredundant.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n userdiff.c | 18 ++++++------------\n 1 file changed, 6 insertions(+), 12 deletions(-)\n\ndiff --git a/userdiff.c b/userdiff.c\nindex 3f81a2261c5..c147bcbb173 100644\n--- a/userdiff.c\n+++ b/userdiff.c\n@@ -275,19 +275,12 @@ static int parse_funcname(struct userdiff_funcname *f, const char *k,\n \treturn 0;\n }\n \n-static int parse_tristate(int *b, const char *k, const char *v)\n+static void parse_tristate(int *b, const char *k, const char *v)\n {\n \tif (v && !strcasecmp(v, \"auto\"))\n \t\t*b = -1;\n \telse\n \t\t*b = git_config_bool(k, v);\n-\treturn 0;\n-}\n-\n-static int parse_bool(int *b, const char *k, const char *v)\n-{\n-\t*b = git_config_bool(k, v);\n-\treturn 0;\n }\n \n int userdiff_config(const char *k, const char *v)\n@@ -312,16 +305,17 @@ int userdiff_config(const char *k, const char *v)\n \t\treturn parse_funcname(&drv->funcname, k, v, 0);\n \tif (!strcmp(type, \"xfuncname\"))\n \t\treturn parse_funcname(&drv->funcname, k, v, REG_EXTENDED);\n-\tif (!strcmp(type, \"binary\"))\n-\t\treturn parse_tristate(&drv->binary, k, v);\n \tif (!strcmp(type, \"command\"))\n \t\treturn git_config_string(&drv->external, k, v);\n \tif (!strcmp(type, \"textconv\"))\n \t\treturn git_config_string(&drv->textconv, k, v);\n-\tif (!strcmp(type, \"cachetextconv\"))\n-\t\treturn parse_bool(&drv->textconv_want_cache, k, v);\n \tif (!strcmp(type, \"wordregex\"))\n \t\treturn git_config_string(&drv->word_regex, k, v);\n+\t/* Don't care about the parse errors for these, fallthrough */\n+\tif (!strcmp(type, \"cachetextconv\"))\n+\t\tdrv->textconv_want_cache = git_config_bool(k, v);\n+\tif (!strcmp(type, \"binary\"))\n+\t\tparse_tristate(&drv->binary, k, v);\n \n \treturn 0;\n }\n-- \n2.31.0.366.ga80606b22c1\n\n"},{"id":"420086","messageId":"cover-00.11-00000000000-20210324T014604Z-avarab@gmail.com","threadId":"55144","inReplyTo":"20210224195129.4004-1-avarab@gmail.com","subject":"[PATCH v4 00/10] userdiff: refactor + test improvements","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-24T01:48:42Z","receivedAt":"2021-03-24T01:50:04Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"This is a restart of the 35-patch v3 of this topic at\nhttps://lore.kernel.org/git/20210224195129.4004-1-avarab@gmail.com/\n\nI still plan on submitting the rest of it, but wanted to start with\nthe early parts of that series that hasn't been controversial or has\noutstanding feedback I haven't addressed.\n\nThe range-diff to v3 is just for those patches I'm re-rolling here.\n\nÆvar Arnfjörð Bjarmason (10):\n  userdiff: refactor away the parse_bool() function\n  userdiff style: re-order drivers in alphabetical order\n  userdiff style: declare patterns with consistent style\n  userdiff style: normalize pascal regex declaration\n  userdiff: add and use for_each_userdiff_driver()\n  userdiff tests: explicitly test \"default\" pattern\n  userdiff tests: list builtin drivers via test-tool\n  userdiff: remove support for \"broken\" tests\n  blame tests: don't rely on t/t4018/ directory\n  blame tests: simplify userdiff driver test\n\n Makefile                 |   1 +\n t/annotate-tests.sh      |  34 ++++----\n t/helper/test-tool.c     |   1 +\n t/helper/test-tool.h     |   1 +\n t/helper/test-userdiff.c |  31 +++++++\n t/t4018-diff-funcname.sh |  39 ++-------\n t/t4018/README           |   3 -\n userdiff.c               | 178 ++++++++++++++++++++++++---------------\n userdiff.h               |  15 ++++\n 9 files changed, 186 insertions(+), 117 deletions(-)\n create mode 100644 t/helper/test-userdiff.c\n\nRange-diff:\n 1:  0be132b05e2 =  1:  fb7346cd296 userdiff: refactor away the parse_bool() function\n 2:  d1e00a739ac =  2:  149387155bc userdiff style: re-order drivers in alphabetical order\n 3:  b99bd158d45 =  3:  faf1a824f05 userdiff style: declare patterns with consistent style\n 4:  9ce6d47021c =  4:  1e9ddcd1a9a userdiff style: normalize pascal regex declaration\n 5:  369fbdcee83 =  5:  64ea5e8443f userdiff: add and use for_each_userdiff_driver()\n 6:  70d62a97211 =  6:  862f6ab5d66 userdiff tests: explicitly test \"default\" pattern\n 7:  792421a2f8b =  7:  22a07591b76 userdiff tests: list builtin drivers via test-tool\n 8:  9081e2a152e !  8:  7755db95014 userdiff: remove support for \"broken\" tests\n    @@ Commit message\n     \n         There have been no \"broken\" tests since 75c3b6b2e8 (userdiff: improve\n         Fortran xfuncname regex, 2020-08-12). Let's remove the test support\n    -    for them, this is in preparation for a more general refactoring of the\n    -    tests.\n    +    for them.\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n 9:  d3652f95d5e !  9:  4e0b4b42e16 blame tests: don't rely on t/t4018/ directory\n    @@ Commit message\n         with userdiff driver, 2020-11-01) so that the blame tests don't rely\n         on stealing the contents of \"t/t4018/fortran-external-function\".\n     \n    -    I'm about to change that file in a subsequent commit. Just moving the\n    -    relevant test file here inline is the easiest solution, and I think\n    -    also the most readable.\n    +    I have another patch series that'll possibly (or not) refactor that\n    +    file, but having this test inter-dependency makes things simple in any\n    +    case by making this test more readable.\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n10:  35d12779ea1 ! 10:  ce98c61bf40 blame tests: simplify userdiff driver test\n    @@ Commit message\n         test_commit, 2021-01-12).\n     \n         We also did not need the full fortran-external-function content. Let's\n    -    cut it down to just the important parts, and further modify it to\n    -    demonstrate that the fortran-specific userdiff function is in effect\n    -    by adding \"DO NOT MATCH ...\" and \"AS THE ...\" lines surrounding the\n    -    \"RIGHT\" one. This is to check that we're using the userdiff \"fortran\"\n    -    driver, as opposed to the default driver.\n    +    cut it down to just the important parts.\n     \n    -    The test also left behind a .gitattributes files, let's clean it up\n    -    with \"test_when_finished\".\n    +    I'm modifying it to demonstrate that the fortran-specific userdiff\n    +    function is in effect by adding \"DO NOT MATCH ...\" and \"AS THE ...\"\n    +    lines surrounding the \"RIGHT\" one.\n    +\n    +    This is to check that we're using the userdiff \"fortran\" driver, as\n    +    opposed to the default driver which would match on those lines as part\n    +    of the general heuristic of matching a line that doesn't begin with\n    +    whitespace.\n    +\n    +    The test had also been leaving behind a .gitattributes file for later\n    +    tests to possibly trip over, let's clean it up with\n    +    \"test_when_finished\".\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n-- \n2.31.0.366.ga80606b22c1\n\n"},{"id":"420087","messageId":"patch-02.11-149387155bc-20210324T014604Z-avarab@gmail.com","threadId":"55144","inReplyTo":"cover-00.11-00000000000-20210324T014604Z-avarab@gmail.com","subject":"[PATCH v4 02/10] userdiff style: re-order drivers in alphabetical order","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-24T01:48:44Z","receivedAt":"2021-03-24T01:50:04Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Address some old code smell and move around the built-in userdiff\ndrivers so they're both in alphabetical order, and now in the same\norder they appear in the gitattributes(5) documentation.\n\nThe two started drifting in be58e70dba (diff: unify external diff and\nfuncname parsing code, 2008-10-05), and then even further in\n80c49c3de2 (color-words: make regex configurable via attributes,\n2009-01-17) when the \"cpp\" pattern was added.\n\nThere are no functional changes here, and as --color-moved will show\nonly moved existing lines.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n userdiff.c | 76 +++++++++++++++++++++++++++---------------------------\n 1 file changed, 38 insertions(+), 38 deletions(-)\n\ndiff --git a/userdiff.c b/userdiff.c\nindex c147bcbb173..c92cbcc0540 100644\n--- a/userdiff.c\n+++ b/userdiff.c\n@@ -44,6 +44,44 @@ PATTERNS(\"bash\",\n \t /* -- */\n \t /* Characters not in the default $IFS value */\n \t \"[^ \\t]+\"),\n+PATTERNS(\"bibtex\", \"(@[a-zA-Z]{1,}[ \\t]*\\\\{{0,1}[ \\t]*[^ \\t\\\"@',\\\\#}{~%]*).*$\",\n+\t \"[={}\\\"]|[^={}\\\" \\t]+\"),\n+PATTERNS(\"cpp\",\n+\t /* Jump targets or access declarations */\n+\t \"!^[ \\t]*[A-Za-z_][A-Za-z_0-9]*:[[:space:]]*($|/[/*])\\n\"\n+\t /* functions/methods, variables, and compounds at top level */\n+\t \"^((::[[:space:]]*)?[A-Za-z_].*)$\",\n+\t /* -- */\n+\t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n+\t \"|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lLuU]*\"\n+\t \"|[-+*/<>%&^|=!]=|--|\\\\+\\\\+|<<=?|>>=?|&&|\\\\|\\\\||::|->\\\\*?|\\\\.\\\\*\"),\n+PATTERNS(\"csharp\",\n+\t /* Keywords */\n+\t \"!^[ \\t]*(do|while|for|if|else|instanceof|new|return|switch|case|throw|catch|using)\\n\"\n+\t /* Methods and constructors */\n+\t \"^[ \\t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe|async)[ \\t]+)*[][<>@.~_[:alnum:]]+[ \\t]+[<>@._[:alnum:]]+[ \\t]*\\\\(.*\\\\))[ \\t]*$\\n\"\n+\t /* Properties */\n+\t \"^[ \\t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \\t]+)*[][<>@.~_[:alnum:]]+[ \\t]+[@._[:alnum:]]+)[ \\t]*$\\n\"\n+\t /* Type definitions */\n+\t \"^[ \\t]*(((static|public|internal|private|protected|new|unsafe|sealed|abstract|partial)[ \\t]+)*(class|enum|interface|struct)[ \\t]+.*)$\\n\"\n+\t /* Namespace */\n+\t \"^[ \\t]*(namespace[ \\t]+.*)$\",\n+\t /* -- */\n+\t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n+\t \"|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?\"\n+\t \"|[-+*/<>%&^|=!]=|--|\\\\+\\\\+|<<=?|>>=?|&&|\\\\|\\\\||::|->\"),\n+IPATTERN(\"css\",\n+\t \"![:;][[:space:]]*$\\n\"\n+\t \"^[:[@.#]?[_a-z0-9].*$\",\n+\t /* -- */\n+\t /*\n+\t  * This regex comes from W3C CSS specs. Should theoretically also\n+\t  * allow ISO 10646 characters U+00A0 and higher,\n+\t  * but they are not handled in this regex.\n+\t  */\n+\t \"-?[_a-zA-Z][-_a-zA-Z0-9]*\" /* identifiers */\n+\t \"|-?[0-9]+|\\\\#[0-9a-fA-F]+\" /* numbers */\n+),\n PATTERNS(\"dts\",\n \t \"!;\\n\"\n \t \"!=\\n\"\n@@ -191,46 +229,8 @@ PATTERNS(\"rust\",\n \t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n \t \"|[0-9][0-9_a-fA-Fiosuxz]*(\\\\.([0-9]*[eE][+-]?)?[0-9_fF]*)?\"\n \t \"|[-+*\\\\/<>%&^|=!:]=|<<=?|>>=?|&&|\\\\|\\\\||->|=>|\\\\.{2}=|\\\\.{3}|::\"),\n-PATTERNS(\"bibtex\", \"(@[a-zA-Z]{1,}[ \\t]*\\\\{{0,1}[ \\t]*[^ \\t\\\"@',\\\\#}{~%]*).*$\",\n-\t \"[={}\\\"]|[^={}\\\" \\t]+\"),\n PATTERNS(\"tex\", \"^(\\\\\\\\((sub)*section|chapter|part)\\\\*{0,1}\\\\{.*)$\",\n \t \"\\\\\\\\[a-zA-Z@]+|\\\\\\\\.|[a-zA-Z0-9\\x80-\\xff]+\"),\n-PATTERNS(\"cpp\",\n-\t /* Jump targets or access declarations */\n-\t \"!^[ \\t]*[A-Za-z_][A-Za-z_0-9]*:[[:space:]]*($|/[/*])\\n\"\n-\t /* functions/methods, variables, and compounds at top level */\n-\t \"^((::[[:space:]]*)?[A-Za-z_].*)$\",\n-\t /* -- */\n-\t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n-\t \"|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lLuU]*\"\n-\t \"|[-+*/<>%&^|=!]=|--|\\\\+\\\\+|<<=?|>>=?|&&|\\\\|\\\\||::|->\\\\*?|\\\\.\\\\*\"),\n-PATTERNS(\"csharp\",\n-\t /* Keywords */\n-\t \"!^[ \\t]*(do|while|for|if|else|instanceof|new|return|switch|case|throw|catch|using)\\n\"\n-\t /* Methods and constructors */\n-\t \"^[ \\t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe|async)[ \\t]+)*[][<>@.~_[:alnum:]]+[ \\t]+[<>@._[:alnum:]]+[ \\t]*\\\\(.*\\\\))[ \\t]*$\\n\"\n-\t /* Properties */\n-\t \"^[ \\t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \\t]+)*[][<>@.~_[:alnum:]]+[ \\t]+[@._[:alnum:]]+)[ \\t]*$\\n\"\n-\t /* Type definitions */\n-\t \"^[ \\t]*(((static|public|internal|private|protected|new|unsafe|sealed|abstract|partial)[ \\t]+)*(class|enum|interface|struct)[ \\t]+.*)$\\n\"\n-\t /* Namespace */\n-\t \"^[ \\t]*(namespace[ \\t]+.*)$\",\n-\t /* -- */\n-\t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n-\t \"|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?\"\n-\t \"|[-+*/<>%&^|=!]=|--|\\\\+\\\\+|<<=?|>>=?|&&|\\\\|\\\\||::|->\"),\n-IPATTERN(\"css\",\n-\t \"![:;][[:space:]]*$\\n\"\n-\t \"^[:[@.#]?[_a-z0-9].*$\",\n-\t /* -- */\n-\t /*\n-\t  * This regex comes from W3C CSS specs. Should theoretically also\n-\t  * allow ISO 10646 characters U+00A0 and higher,\n-\t  * but they are not handled in this regex.\n-\t  */\n-\t \"-?[_a-zA-Z][-_a-zA-Z0-9]*\" /* identifiers */\n-\t \"|-?[0-9]+|\\\\#[0-9a-fA-F]+\" /* numbers */\n-),\n { \"default\", NULL, -1, { NULL, 0 } },\n };\n #undef PATTERNS\n-- \n2.31.0.366.ga80606b22c1\n\n"},{"id":"420088","messageId":"patch-06.11-862f6ab5d66-20210324T014604Z-avarab@gmail.com","threadId":"55144","inReplyTo":"cover-00.11-00000000000-20210324T014604Z-avarab@gmail.com","subject":"[PATCH v4 06/10] userdiff tests: explicitly test \"default\" pattern","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-24T01:48:48Z","receivedAt":"2021-03-24T01:50:04Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Since 122aa6f9c0 (diff: introduce diff.<driver>.binary, 2008-10-05)\nthe internals of the userdiff.c code have understood a \"default\" name,\nwhich is invoked as userdiff_find_by_name(\"default\") and present in\nthe \"builtin_drivers\" struct. Let's test for this special case.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018-diff-funcname.sh | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex 9675bc17db2..cefe329aea7 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -49,6 +49,7 @@ diffpatterns=\"\n \truby\n \trust\n \ttex\n+\tdefault\n \tcustom1\n \tcustom2\n \tcustom3\n-- \n2.31.0.366.ga80606b22c1\n\n"},{"id":"420089","messageId":"patch-04.11-1e9ddcd1a9a-20210324T014604Z-avarab@gmail.com","threadId":"55144","inReplyTo":"cover-00.11-00000000000-20210324T014604Z-avarab@gmail.com","subject":"[PATCH v4 04/10] userdiff style: normalize pascal regex declaration","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-24T01:48:46Z","receivedAt":"2021-03-24T01:50:04Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Declare the pascal pattern consistently with how we declare the\nothers, not having \"\\n\" on one line by itself, but as part of the\npattern, and when there are alterations have the \"|\" at the start, not\nend of the line.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n userdiff.c | 5 ++---\n 1 file changed, 2 insertions(+), 3 deletions(-)\n\ndiff --git a/userdiff.c b/userdiff.c\nindex c7aaf7094f8..10a02d36209 100644\n--- a/userdiff.c\n+++ b/userdiff.c\n@@ -175,9 +175,8 @@ PATTERNS(\"objc\",\n \t \"|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?\"\n \t \"|[-+*/<>%&^|=!]=|--|\\\\+\\\\+|<<=?|>>=?|&&|\\\\|\\\\||::|->\"),\n PATTERNS(\"pascal\",\n-\t \"^(((class[ \\t]+)?(procedure|function)|constructor|destructor|interface|\"\n-\t\t\"implementation|initialization|finalization)[ \\t]*.*)$\"\n-\t \"\\n\"\n+\t \"^(((class[ \\t]+)?(procedure|function)|constructor|destructor|interface\"\n+\t \"|implementation|initialization|finalization)[ \\t]*.*)$\\n\"\n \t \"^(.*=[ \\t]*(class|record).*)$\",\n \t /* -- */\n \t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n-- \n2.31.0.366.ga80606b22c1\n\n"},{"id":"420090","messageId":"patch-08.11-7755db95014-20210324T014604Z-avarab@gmail.com","threadId":"55144","inReplyTo":"cover-00.11-00000000000-20210324T014604Z-avarab@gmail.com","subject":"[PATCH v4 08/10] userdiff: remove support for \"broken\" tests","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-24T01:48:50Z","receivedAt":"2021-03-24T01:50:04Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"There have been no \"broken\" tests since 75c3b6b2e8 (userdiff: improve\nFortran xfuncname regex, 2020-08-12). Let's remove the test support\nfor them.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018-diff-funcname.sh | 8 +-------\n t/t4018/README           | 3 ---\n 2 files changed, 1 insertion(+), 10 deletions(-)\n\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex 5bd82e09ab3..9aec9f8e6de 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -86,13 +86,7 @@ test_expect_success 'setup hunk header tests' '\n # check each individual file\n for i in $(git ls-files)\n do\n-\tif grep broken \"$i\" >/dev/null 2>&1\n-\tthen\n-\t\tresult=failure\n-\telse\n-\t\tresult=success\n-\tfi\n-\ttest_expect_$result \"hunk header: $i\" \"\n+\ttest_expect_success \"hunk header: $i\" \"\n \t\tgit diff -U1 $i >actual &&\n \t\tgrep '@@ .* @@.*RIGHT' actual\n \t\"\ndiff --git a/t/t4018/README b/t/t4018/README\nindex 283e01cca1a..2d25b2b4fc9 100644\n--- a/t/t4018/README\n+++ b/t/t4018/README\n@@ -7,9 +7,6 @@ at least two lines from the line that must appear in the hunk header.\n The text that must appear in the hunk header must contain the word\n \"right\", but in all upper-case, like in the title above.\n \n-To mark a test case that highlights a malfunction, insert the word\n-BROKEN in all lower-case somewhere in the file.\n-\n This text is a bit twisted and out of order, but it is itself a\n test case for the default hunk header pattern. Know what you are doing\n if you change it.\n-- \n2.31.0.366.ga80606b22c1\n\n"},{"id":"420091","messageId":"patch-05.11-64ea5e8443f-20210324T014604Z-avarab@gmail.com","threadId":"55144","inReplyTo":"cover-00.11-00000000000-20210324T014604Z-avarab@gmail.com","subject":"[PATCH v4 05/10] userdiff: add and use for_each_userdiff_driver()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-24T01:48:47Z","receivedAt":"2021-03-24T01:50:04Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Refactor the userdiff_find_by_namelen() function so that a new\nfor_each_userdiff_driver() API function does most of the work.\n\nThis will be useful for the same reason we've got other for_each_*()\nAPI functions as part of various APIs, and will be used in a follow-up\ncommit.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n userdiff.c | 61 +++++++++++++++++++++++++++++++++++++++++++-----------\n userdiff.h | 15 ++++++++++++++\n 2 files changed, 64 insertions(+), 12 deletions(-)\n\ndiff --git a/userdiff.c b/userdiff.c\nindex 10a02d36209..55f4f769bd3 100644\n--- a/userdiff.c\n+++ b/userdiff.c\n@@ -259,20 +259,32 @@ static struct userdiff_driver driver_false = {\n \t{ NULL, 0 }\n };\n \n-static struct userdiff_driver *userdiff_find_by_namelen(const char *k, size_t len)\n+struct for_each_userdiff_driver_cb {\n+\tconst char *k;\n+\tsize_t len;\n+\tstruct userdiff_driver *driver;\n+};\n+\n+static int userdiff_find_by_namelen_cb(struct userdiff_driver *driver,\n+\t\t\t\t       enum userdiff_driver_type type, void *priv)\n {\n-\tint i;\n-\tfor (i = 0; i < ndrivers; i++) {\n-\t\tstruct userdiff_driver *drv = drivers + i;\n-\t\tif (!strncmp(drv->name, k, len) && !drv->name[len])\n-\t\t\treturn drv;\n-\t}\n-\tfor (i = 0; i < ARRAY_SIZE(builtin_drivers); i++) {\n-\t\tstruct userdiff_driver *drv = builtin_drivers + i;\n-\t\tif (!strncmp(drv->name, k, len) && !drv->name[len])\n-\t\t\treturn drv;\n+\tstruct for_each_userdiff_driver_cb *cb_data = priv;\n+\n+\tif (!strncmp(driver->name, cb_data->k, cb_data->len) &&\n+\t    !driver->name[cb_data->len]) {\n+\t\tcb_data->driver = driver;\n+\t\treturn -1; /* found it! */\n \t}\n-\treturn NULL;\n+\treturn 0;\n+}\n+\n+static struct userdiff_driver *userdiff_find_by_namelen(const char *k, size_t len)\n+{\n+\tstruct for_each_userdiff_driver_cb udcbdata = { .k = k, .len = len, .driver = NULL };\n+\n+\tfor_each_userdiff_driver(userdiff_find_by_namelen_cb,\n+\t\t\t\t USERDIFF_DRIVER_TYPE_UNSPECIFIED, &udcbdata);\n+\treturn udcbdata.driver;\n }\n \n static int parse_funcname(struct userdiff_funcname *f, const char *k,\n@@ -373,3 +385,28 @@ struct userdiff_driver *userdiff_get_textconv(struct repository *r,\n \n \treturn driver;\n }\n+\n+int for_each_userdiff_driver(each_userdiff_driver_fn fn,\n+\t\t\t     enum userdiff_driver_type type, void *cb_data)\n+{\n+\tint i, ret;\n+\tif (type & (USERDIFF_DRIVER_TYPE_UNSPECIFIED | USERDIFF_DRIVER_TYPE_CUSTOM)) {\n+\n+\t\tfor (i = 0; i < ndrivers; i++) {\n+\t\t\tstruct userdiff_driver *drv = drivers + i;\n+\t\t\tret = fn(drv, USERDIFF_DRIVER_TYPE_CUSTOM, cb_data);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\t\t}\n+\t}\n+\tif (type & (USERDIFF_DRIVER_TYPE_UNSPECIFIED | USERDIFF_DRIVER_TYPE_BUILTIN)) {\n+\n+\t\tfor (i = 0; i < ARRAY_SIZE(builtin_drivers); i++) {\n+\t\t\tstruct userdiff_driver *drv = builtin_drivers + i;\n+\t\t\tret = fn(drv, USERDIFF_DRIVER_TYPE_BUILTIN, cb_data);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\t\t}\n+\t}\n+\treturn 0;\n+}\ndiff --git a/userdiff.h b/userdiff.h\nindex 203057e13e5..fe14014a775 100644\n--- a/userdiff.h\n+++ b/userdiff.h\n@@ -21,6 +21,13 @@ struct userdiff_driver {\n \tstruct notes_cache *textconv_cache;\n \tint textconv_want_cache;\n };\n+enum userdiff_driver_type {\n+\tUSERDIFF_DRIVER_TYPE_UNSPECIFIED = 1<<0,\n+\tUSERDIFF_DRIVER_TYPE_BUILTIN = 1<<1,\n+\tUSERDIFF_DRIVER_TYPE_CUSTOM = 1<<2,\n+};\n+typedef int (*each_userdiff_driver_fn)(struct userdiff_driver *,\n+\t\t\t\t       enum userdiff_driver_type, void *);\n \n int userdiff_config(const char *k, const char *v);\n struct userdiff_driver *userdiff_find_by_name(const char *name);\n@@ -34,4 +41,12 @@ struct userdiff_driver *userdiff_find_by_path(struct index_state *istate,\n struct userdiff_driver *userdiff_get_textconv(struct repository *r,\n \t\t\t\t\t      struct userdiff_driver *driver);\n \n+/*\n+ * Iterate over each driver of type userdiff_driver_type, or\n+ * USERDIFF_DRIVER_TYPE_UNSPECIFIED for all of them. Return non-zero\n+ * to exit from the loop.\n+ */\n+int for_each_userdiff_driver(each_userdiff_driver_fn,\n+\t\t\t     enum userdiff_driver_type, void *);\n+\n #endif /* USERDIFF */\n-- \n2.31.0.366.ga80606b22c1\n\n"},{"id":"420095","messageId":"patch-03.11-faf1a824f05-20210324T014604Z-avarab@gmail.com","threadId":"55144","inReplyTo":"cover-00.11-00000000000-20210324T014604Z-avarab@gmail.com","subject":"[PATCH v4 03/10] userdiff style: declare patterns with consistent style","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-24T01:48:45Z","receivedAt":"2021-03-24T01:50:04Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change those patterns which were declared with a regex on the same\nline as the \"PATTERNS()\" line to put that regex on the next line, and\nadd missing \"/* -- */\" separator comments between the pattern and\nword_regex.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n userdiff.c | 20 +++++++++++++++-----\n 1 file changed, 15 insertions(+), 5 deletions(-)\n\ndiff --git a/userdiff.c b/userdiff.c\nindex c92cbcc0540..c7aaf7094f8 100644\n--- a/userdiff.c\n+++ b/userdiff.c\n@@ -44,7 +44,9 @@ PATTERNS(\"bash\",\n \t /* -- */\n \t /* Characters not in the default $IFS value */\n \t \"[^ \\t]+\"),\n-PATTERNS(\"bibtex\", \"(@[a-zA-Z]{1,}[ \\t]*\\\\{{0,1}[ \\t]*[^ \\t\\\"@',\\\\#}{~%]*).*$\",\n+PATTERNS(\"bibtex\",\n+\t \"(@[a-zA-Z]{1,}[ \\t]*\\\\{{0,1}[ \\t]*[^ \\t\\\"@',\\\\#}{~%]*).*$\",\n+\t /* -- */\n \t \"[={}\\\"]|[^={}\\\" \\t]+\"),\n PATTERNS(\"cpp\",\n \t /* Jump targets or access declarations */\n@@ -121,7 +123,9 @@ IPATTERN(\"fortran\",\n \t  * they would have been matched above as a variable anyway. */\n \t \"|[-+]?[0-9.]+([AaIiDdEeFfLlTtXx][Ss]?[-+]?[0-9.]*)?(_[a-zA-Z0-9][a-zA-Z0-9_]*)?\"\n \t \"|//|\\\\*\\\\*|::|[/<>=]=\"),\n-IPATTERN(\"fountain\", \"^((\\\\.[^.]|(int|ext|est|int\\\\.?/ext|i/e)[. ]).*)$\",\n+IPATTERN(\"fountain\",\n+\t \"^((\\\\.[^.]|(int|ext|est|int\\\\.?/ext|i/e)[. ]).*)$\",\n+\t /* -- */\n \t \"[^ \\t-]+\"),\n PATTERNS(\"golang\",\n \t /* Functions */\n@@ -132,7 +136,9 @@ PATTERNS(\"golang\",\n \t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n \t \"|[-+0-9.eE]+i?|0[xX]?[0-9a-fA-F]+i?\"\n \t \"|[-+*/<>%&^|=!:]=|--|\\\\+\\\\+|<<=?|>>=?|&\\\\^=?|&&|\\\\|\\\\||<-|\\\\.{3}\"),\n-PATTERNS(\"html\", \"^[ \\t]*(<[Hh][1-6]([ \\t].*)?>.*)$\",\n+PATTERNS(\"html\",\n+\t \"^[ \\t]*(<[Hh][1-6]([ \\t].*)?>.*)$\",\n+\t /* -- */\n \t \"[^<>= \\t]+\"),\n PATTERNS(\"java\",\n \t \"!^[ \\t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)\\n\"\n@@ -144,6 +150,7 @@ PATTERNS(\"java\",\n \t \"|--|\\\\+\\\\+|<<=?|>>>?=?|&&|\\\\|\\\\|\"),\n PATTERNS(\"markdown\",\n \t \"^ {0,3}#{1,6}[ \\t].*\",\n+\t /* -- */\n \t \"[^<>= \\t]+\"),\n PATTERNS(\"matlab\",\n \t /*\n@@ -152,6 +159,7 @@ PATTERNS(\"matlab\",\n \t  * that is understood by both.\n \t  */\n \t \"^[[:space:]]*((classdef|function)[[:space:]].*)$|^(%%%?|##)[[:space:]].*$\",\n+\t /* -- */\n \t \"[a-zA-Z_][a-zA-Z0-9_]*|[-+0-9.e]+|[=~<>]=|\\\\.[*/\\\\^']|\\\\|\\\\||&&\"),\n PATTERNS(\"objc\",\n \t /* Negate C statements that can look like functions */\n@@ -212,13 +220,15 @@ PATTERNS(\"php\",\n \t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n \t \"|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+\"\n \t \"|[-+*/<>%&^|=!.]=|--|\\\\+\\\\+|<<=?|>>=?|===|&&|\\\\|\\\\||::|->\"),\n-PATTERNS(\"python\", \"^[ \\t]*((class|(async[ \\t]+)?def)[ \\t].*)$\",\n+PATTERNS(\"python\",\n+\t \"^[ \\t]*((class|(async[ \\t]+)?def)[ \\t].*)$\",\n \t /* -- */\n \t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n \t \"|[-+0-9.e]+[jJlL]?|0[xX]?[0-9a-fA-F]+[lL]?\"\n \t \"|[-+*/<>%&^|=!]=|//=?|<<=?|>>=?|\\\\*\\\\*=?\"),\n \t /* -- */\n-PATTERNS(\"ruby\", \"^[ \\t]*((class|module|def)[ \\t].*)$\",\n+PATTERNS(\"ruby\",\n+\t \"^[ \\t]*((class|module|def)[ \\t].*)$\",\n \t /* -- */\n \t \"(@|@@|\\\\$)?[a-zA-Z_][a-zA-Z0-9_]*\"\n \t \"|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+|\\\\?(\\\\\\\\C-)?(\\\\\\\\M-)?.\"\n-- \n2.31.0.366.ga80606b22c1\n\n"},{"id":"420092","messageId":"patch-09.11-4e0b4b42e16-20210324T014604Z-avarab@gmail.com","threadId":"55144","inReplyTo":"cover-00.11-00000000000-20210324T014604Z-avarab@gmail.com","subject":"[PATCH v4 09/10] blame tests: don't rely on t/t4018/ directory","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-24T01:48:51Z","receivedAt":"2021-03-24T01:50:05Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Refactor a test added in 9466e3809d (blame: enable funcname blaming\nwith userdiff driver, 2020-11-01) so that the blame tests don't rely\non stealing the contents of \"t/t4018/fortran-external-function\".\n\nI have another patch series that'll possibly (or not) refactor that\nfile, but having this test inter-dependency makes things simple in any\ncase by making this test more readable.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/annotate-tests.sh | 16 +++++++++++++---\n 1 file changed, 13 insertions(+), 3 deletions(-)\n\ndiff --git a/t/annotate-tests.sh b/t/annotate-tests.sh\nindex 29ce89090d8..04a2c58594c 100644\n--- a/t/annotate-tests.sh\n+++ b/t/annotate-tests.sh\n@@ -482,12 +482,22 @@ test_expect_success 'blame -L ^:RE (absolute: end-of-file)' '\n test_expect_success 'setup -L :funcname with userdiff driver' '\n \techo \"fortran-* diff=fortran\" >.gitattributes &&\n \tfortran_file=fortran-external-function &&\n-\torig_file=\"$TEST_DIRECTORY/t4018/$fortran_file\" &&\n-\tcp \"$orig_file\" . &&\n+\tcat >$fortran_file <<-\\EOF &&\n+\tfunction RIGHT(a, b) result(c)\n+\n+\tinteger, intent(in) :: ChangeMe\n+\tinteger, intent(in) :: b\n+\tinteger, intent(out) :: c\n+\n+\tc = a+b\n+\n+\tend function RIGHT\n+\tEOF\n \tgit add \"$fortran_file\" &&\n \tGIT_AUTHOR_NAME=\"A\" GIT_AUTHOR_EMAIL=\"A@test.git\" \\\n \tgit commit -m \"add fortran file\" &&\n-\tsed -e \"s/ChangeMe/IWasChanged/\" <\"$orig_file\" >\"$fortran_file\" &&\n+\tsed -e \"s/ChangeMe/IWasChanged/\" <\"$fortran_file\" >\"$fortran_file\".tmp &&\n+\tmv \"$fortran_file\".tmp \"$fortran_file\" &&\n \tgit add \"$fortran_file\" &&\n \tGIT_AUTHOR_NAME=\"B\" GIT_AUTHOR_EMAIL=\"B@test.git\" \\\n \tgit commit -m \"change fortran file\"\n-- \n2.31.0.366.ga80606b22c1\n\n"},{"id":"420093","messageId":"patch-07.11-22a07591b76-20210324T014604Z-avarab@gmail.com","threadId":"55144","inReplyTo":"cover-00.11-00000000000-20210324T014604Z-avarab@gmail.com","subject":"[PATCH v4 07/10] userdiff tests: list builtin drivers via test-tool","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-24T01:48:49Z","receivedAt":"2021-03-24T01:50:05Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the userdiff test to list the builtin drivers via the\ntest-tool, using the new for_each_userdiff_driver() API function.\n\nThis gets rid of the need to modify this part of the test every time a\nnew pattern is added, see 2ff6c34612 (userdiff: support Bash,\n2020-10-22) and 09dad9256a (userdiff: support Markdown, 2020-05-02)\nfor two recent examples.\n\nI only need the \"list-builtin-drivers \"argument here, but let's add\n\"list-custom-drivers\" and \"list-drivers\" too, just because it's easy.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Makefile                 |  1 +\n t/helper/test-tool.c     |  1 +\n t/helper/test-tool.h     |  1 +\n t/helper/test-userdiff.c | 31 +++++++++++++++++++++++++++++++\n t/t4018-diff-funcname.sh | 32 ++++++++------------------------\n 5 files changed, 42 insertions(+), 24 deletions(-)\n create mode 100644 t/helper/test-userdiff.c\n\ndiff --git a/Makefile b/Makefile\nindex f3dc2178324..3e2568d68da 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -744,6 +744,7 @@ TEST_BUILTINS_OBJS += test-submodule-nested-repo-config.o\n TEST_BUILTINS_OBJS += test-subprocess.o\n TEST_BUILTINS_OBJS += test-trace2.o\n TEST_BUILTINS_OBJS += test-urlmatch-normalization.o\n+TEST_BUILTINS_OBJS += test-userdiff.o\n TEST_BUILTINS_OBJS += test-wildmatch.o\n TEST_BUILTINS_OBJS += test-windows-named-pipe.o\n TEST_BUILTINS_OBJS += test-write-cache.o\ndiff --git a/t/helper/test-tool.c b/t/helper/test-tool.c\nindex f97cd9f48a6..dcb05ca6e5e 100644\n--- a/t/helper/test-tool.c\n+++ b/t/helper/test-tool.c\n@@ -71,6 +71,7 @@ static struct test_cmd cmds[] = {\n \t{ \"submodule-nested-repo-config\", cmd__submodule_nested_repo_config },\n \t{ \"subprocess\", cmd__subprocess },\n \t{ \"trace2\", cmd__trace2 },\n+\t{ \"userdiff\", cmd__userdiff },\n \t{ \"urlmatch-normalization\", cmd__urlmatch_normalization },\n \t{ \"xml-encode\", cmd__xml_encode },\n \t{ \"wildmatch\", cmd__wildmatch },\ndiff --git a/t/helper/test-tool.h b/t/helper/test-tool.h\nindex 28072c0ad5a..589f2e8ac67 100644\n--- a/t/helper/test-tool.h\n+++ b/t/helper/test-tool.h\n@@ -61,6 +61,7 @@ int cmd__submodule_config(int argc, const char **argv);\n int cmd__submodule_nested_repo_config(int argc, const char **argv);\n int cmd__subprocess(int argc, const char **argv);\n int cmd__trace2(int argc, const char **argv);\n+int cmd__userdiff(int argc, const char **argv);\n int cmd__urlmatch_normalization(int argc, const char **argv);\n int cmd__xml_encode(int argc, const char **argv);\n int cmd__wildmatch(int argc, const char **argv);\ndiff --git a/t/helper/test-userdiff.c b/t/helper/test-userdiff.c\nnew file mode 100644\nindex 00000000000..f173a4f18af\n--- /dev/null\n+++ b/t/helper/test-userdiff.c\n@@ -0,0 +1,31 @@\n+#include \"test-tool.h\"\n+#include \"cache.h\"\n+#include \"userdiff.h\"\n+\n+static int driver_cb(struct userdiff_driver *driver,\n+\t\t     enum userdiff_driver_type type, void *priv)\n+{\n+\tif (driver->funcname.pattern)\n+\t\tputs(driver->name);\n+\treturn 0;\n+}\n+\n+static int list_what(enum userdiff_driver_type type)\n+{\n+\treturn for_each_userdiff_driver(driver_cb, type, NULL);\n+}\n+\n+int cmd__userdiff(int argc, const char **argv)\n+{\n+\tif (argc != 2)\n+\t\treturn 1;\n+\n+\tif (!strcmp(argv[1], \"list-drivers\"))\n+\t\treturn list_what(USERDIFF_DRIVER_TYPE_UNSPECIFIED);\n+\telse if (!strcmp(argv[1], \"list-builtin-drivers\"))\n+\t\treturn list_what(USERDIFF_DRIVER_TYPE_BUILTIN);\n+\telse if (!strcmp(argv[1], \"list-custom-drivers\"))\n+\t\treturn list_what(USERDIFF_DRIVER_TYPE_CUSTOM);\n+\telse\n+\t\treturn error(\"unknown argument %s\", argv[1]);\n+}\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex cefe329aea7..5bd82e09ab3 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -8,6 +8,13 @@ test_description='Test custom diff function name patterns'\n . ./test-lib.sh\n \n test_expect_success 'setup' '\n+\t# Make sure additions to builtin_drivers are sorted\n+\ttest_when_finished \"rm builtin-drivers.sorted\" &&\n+\ttest-tool userdiff list-builtin-drivers >builtin-drivers &&\n+\ttest_file_not_empty builtin-drivers &&\n+\tsort <builtin-drivers >builtin-drivers.sorted &&\n+\ttest_cmp builtin-drivers.sorted builtin-drivers &&\n+\n \t# a non-trivial custom pattern\n \tgit config diff.custom1.funcname \"!static\n !String\n@@ -26,30 +33,7 @@ test_expect_success 'setup' '\n '\n \n diffpatterns=\"\n-\tada\n-\tbash\n-\tbibtex\n-\tcpp\n-\tcsharp\n-\tcss\n-\tdts\n-\telixir\n-\tfortran\n-\tfountain\n-\tgolang\n-\thtml\n-\tjava\n-\tmarkdown\n-\tmatlab\n-\tobjc\n-\tpascal\n-\tperl\n-\tphp\n-\tpython\n-\truby\n-\trust\n-\ttex\n-\tdefault\n+\t$(cat builtin-drivers)\n \tcustom1\n \tcustom2\n \tcustom3\n-- \n2.31.0.366.ga80606b22c1\n\n"},{"id":"420094","messageId":"patch-10.11-ce98c61bf40-20210324T014604Z-avarab@gmail.com","threadId":"55144","inReplyTo":"cover-00.11-00000000000-20210324T014604Z-avarab@gmail.com","subject":"[PATCH v4 10/10] blame tests: simplify userdiff driver test","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-24T01:48:52Z","receivedAt":"2021-03-24T01:50:05Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Simplify the test added in 9466e3809d (blame: enable funcname blaming\nwith userdiff driver, 2020-11-01) to use the --author support recently\nadded in 999cfc4f45 (test-lib functions: add --author support to\ntest_commit, 2021-01-12).\n\nWe also did not need the full fortran-external-function content. Let's\ncut it down to just the important parts.\n\nI'm modifying it to demonstrate that the fortran-specific userdiff\nfunction is in effect by adding \"DO NOT MATCH ...\" and \"AS THE ...\"\nlines surrounding the \"RIGHT\" one.\n\nThis is to check that we're using the userdiff \"fortran\" driver, as\nopposed to the default driver which would match on those lines as part\nof the general heuristic of matching a line that doesn't begin with\nwhitespace.\n\nThe test had also been leaving behind a .gitattributes file for later\ntests to possibly trip over, let's clean it up with\n\"test_when_finished\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/annotate-tests.sh | 36 +++++++++++++++---------------------\n 1 file changed, 15 insertions(+), 21 deletions(-)\n\ndiff --git a/t/annotate-tests.sh b/t/annotate-tests.sh\nindex 04a2c58594c..d3b299e75cb 100644\n--- a/t/annotate-tests.sh\n+++ b/t/annotate-tests.sh\n@@ -479,32 +479,26 @@ test_expect_success 'blame -L ^:RE (absolute: end-of-file)' '\n \tcheck_count -f hello.c -L$n -L^:ma.. F 4 G 1 H 1\n '\n \n-test_expect_success 'setup -L :funcname with userdiff driver' '\n-\techo \"fortran-* diff=fortran\" >.gitattributes &&\n-\tfortran_file=fortran-external-function &&\n-\tcat >$fortran_file <<-\\EOF &&\n+test_expect_success 'blame -L :funcname with userdiff driver' '\n+\tcat >file.template <<-\\EOF &&\n+\tDO NOT MATCH THIS LINE\n \tfunction RIGHT(a, b) result(c)\n+\tAS THE DEFAULT DRIVER WOULD\n \n \tinteger, intent(in) :: ChangeMe\n-\tinteger, intent(in) :: b\n-\tinteger, intent(out) :: c\n-\n-\tc = a+b\n-\n-\tend function RIGHT\n \tEOF\n-\tgit add \"$fortran_file\" &&\n-\tGIT_AUTHOR_NAME=\"A\" GIT_AUTHOR_EMAIL=\"A@test.git\" \\\n-\tgit commit -m \"add fortran file\" &&\n-\tsed -e \"s/ChangeMe/IWasChanged/\" <\"$fortran_file\" >\"$fortran_file\".tmp &&\n-\tmv \"$fortran_file\".tmp \"$fortran_file\" &&\n-\tgit add \"$fortran_file\" &&\n-\tGIT_AUTHOR_NAME=\"B\" GIT_AUTHOR_EMAIL=\"B@test.git\" \\\n-\tgit commit -m \"change fortran file\"\n-'\n \n-test_expect_success 'blame -L :funcname with userdiff driver' '\n-\tcheck_count -f fortran-external-function -L:RIGHT A 7 B 1\n+\tfortran_file=file.f03 &&\n+\ttest_when_finished \"rm .gitattributes\" &&\n+\techo \"$fortran_file diff=fortran\" >.gitattributes &&\n+\n+\ttest_commit --author \"A <A@test.git>\" \\\n+\t\t\"add\" \"$fortran_file\" \\\n+\t\t\"$(cat file.template)\" &&\n+\ttest_commit --author \"B <B@test.git>\" \\\n+\t\t\"change\" \"$fortran_file\" \\\n+\t\t\"$(cat file.template | sed -e s/ChangeMe/IWasChanged/)\" &&\n+\tcheck_count -f \"$fortran_file\" -L:RIGHT A 3 B 1\n '\n \n test_expect_success 'setup incremental' '\n-- \n2.31.0.366.ga80606b22c1\n\n"},{"id":"420099","messageId":"xmqqtup1yx2f.fsf@gitster.g","threadId":"55144","inReplyTo":"patch-05.11-64ea5e8443f-20210324T014604Z-avarab@gmail.com","subject":"Re: [PATCH v4 05/10] userdiff: add and use for_each_userdiff_driver()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-03-24T04:50:32Z","receivedAt":"2021-03-24T04:51:44Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> +struct for_each_userdiff_driver_cb {\n> +\tconst char *k;\n> +\tsize_t len;\n> +\tstruct userdiff_driver *driver;\n> +};\n\nMakes me wonder if we want to rename s/k/name/;\n\n> +static int userdiff_find_by_namelen_cb(struct userdiff_driver *driver,\n> +\t\t\t\t       enum userdiff_driver_type type, void *priv)\n>  {\n> +\tstruct for_each_userdiff_driver_cb *cb_data = priv;\n> +\n> +\tif (!strncmp(driver->name, cb_data->k, cb_data->len) &&\n> +\t    !driver->name[cb_data->len]) {\n> +\t\tcb_data->driver = driver;\n> +\t\treturn -1; /* found it! */\n>  \t}\n> -\treturn NULL;\n> +\treturn 0;\n> +}\n\nMakes sense.\n\n> +static struct userdiff_driver *userdiff_find_by_namelen(const char *k, size_t len)\n> +{\n> +\tstruct for_each_userdiff_driver_cb udcbdata = { .k = k, .len = len, .driver = NULL };\n\nNo need to explicitly spell the zero initialization.  Wrapping it\nlike this:\n\n\tstruct for_each_userdiff_driver_cb udcbdata = {\n\t\t.k = k, .len = len\n\t};\n\nwould avoid the overlong line.\n\n> +\n> +\tfor_each_userdiff_driver(userdiff_find_by_namelen_cb,\n> +\t\t\t\t USERDIFF_DRIVER_TYPE_UNSPECIFIED, &udcbdata);\n> +\treturn udcbdata.driver;\n\n\n> @@ -373,3 +385,28 @@ struct userdiff_driver *userdiff_get_textconv(struct repository *r,\n>  \n>  \treturn driver;\n>  }\n> +\n> +int for_each_userdiff_driver(each_userdiff_driver_fn fn,\n> +\t\t\t     enum userdiff_driver_type type, void *cb_data)\n> +{\n> +\tint i, ret;\n> +\tif (type & (USERDIFF_DRIVER_TYPE_UNSPECIFIED | USERDIFF_DRIVER_TYPE_CUSTOM)) {\n\nI presume that the concrete ones are bitmask (i.e. BUILTIN occupies\nbit #0 while CUSTOM occupies bit #1, or something like that).  Then\n\n    #define USERDIFF_DRIVER_TYPE_UNSPECIFIED (-1)\n\nwould make this (and the other) condition far easier to read, i.e.\n\n\tif (type & USERDIFF_DRIVER_TYPE_CUSTOM) {\n\t\t... if the caller wants to iterate over \"custom\" drivers\n\t\t... do these things\n\n"},{"id":"420125","messageId":"7c560336-c087-5159-06c2-5b22e949902e@kdbg.org","threadId":"55144","inReplyTo":"cover-00.11-00000000000-20210324T014604Z-avarab@gmail.com","subject":"Re: [PATCH v4 00/10] userdiff: refactor + test improvements","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2021-03-24T17:19:54Z","receivedAt":"2021-03-24T18:09:05Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 24.03.21 um 02:48 schrieb Ævar Arnfjörð Bjarmason:\n> This is a restart of the 35-patch v3 of this topic at\n> https://lore.kernel.org/git/20210224195129.4004-1-avarab@gmail.com/\n> \n> I still plan on submitting the rest of it, but wanted to start with\n> the early parts of that series that hasn't been controversial or has\n> outstanding feedback I haven't addressed.\n> \n> The range-diff to v3 is just for those patches I'm re-rolling here.\n\nI'm mostly relying on the interdiff below. I think I had no comments on\nthese patches in the earlier round, so:\n\nAcked-by: Johannes Sixt <j6t@kdbg.org>\n\n> \n> Ævar Arnfjörð Bjarmason (10):\n>   userdiff: refactor away the parse_bool() function\n>   userdiff style: re-order drivers in alphabetical order\n>   userdiff style: declare patterns with consistent style\n>   userdiff style: normalize pascal regex declaration\n>   userdiff: add and use for_each_userdiff_driver()\n>   userdiff tests: explicitly test \"default\" pattern\n>   userdiff tests: list builtin drivers via test-tool\n>   userdiff: remove support for \"broken\" tests\n>   blame tests: don't rely on t/t4018/ directory\n>   blame tests: simplify userdiff driver test\n> \n>  Makefile                 |   1 +\n>  t/annotate-tests.sh      |  34 ++++----\n>  t/helper/test-tool.c     |   1 +\n>  t/helper/test-tool.h     |   1 +\n>  t/helper/test-userdiff.c |  31 +++++++\n>  t/t4018-diff-funcname.sh |  39 ++-------\n>  t/t4018/README           |   3 -\n>  userdiff.c               | 178 ++++++++++++++++++++++++---------------\n>  userdiff.h               |  15 ++++\n>  9 files changed, 186 insertions(+), 117 deletions(-)\n>  create mode 100644 t/helper/test-userdiff.c\n> \n> Range-diff:\n>  1:  0be132b05e2 =  1:  fb7346cd296 userdiff: refactor away the parse_bool() function\n>  2:  d1e00a739ac =  2:  149387155bc userdiff style: re-order drivers in alphabetical order\n>  3:  b99bd158d45 =  3:  faf1a824f05 userdiff style: declare patterns with consistent style\n>  4:  9ce6d47021c =  4:  1e9ddcd1a9a userdiff style: normalize pascal regex declaration\n>  5:  369fbdcee83 =  5:  64ea5e8443f userdiff: add and use for_each_userdiff_driver()\n>  6:  70d62a97211 =  6:  862f6ab5d66 userdiff tests: explicitly test \"default\" pattern\n>  7:  792421a2f8b =  7:  22a07591b76 userdiff tests: list builtin drivers via test-tool\n>  8:  9081e2a152e !  8:  7755db95014 userdiff: remove support for \"broken\" tests\n>     @@ Commit message\n>      \n>          There have been no \"broken\" tests since 75c3b6b2e8 (userdiff: improve\n>          Fortran xfuncname regex, 2020-08-12). Let's remove the test support\n>     -    for them, this is in preparation for a more general refactoring of the\n>     -    tests.\n>     +    for them.\n>      \n>          Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>      \n>  9:  d3652f95d5e !  9:  4e0b4b42e16 blame tests: don't rely on t/t4018/ directory\n>     @@ Commit message\n>          with userdiff driver, 2020-11-01) so that the blame tests don't rely\n>          on stealing the contents of \"t/t4018/fortran-external-function\".\n>      \n>     -    I'm about to change that file in a subsequent commit. Just moving the\n>     -    relevant test file here inline is the easiest solution, and I think\n>     -    also the most readable.\n>     +    I have another patch series that'll possibly (or not) refactor that\n>     +    file, but having this test inter-dependency makes things simple in any\n>     +    case by making this test more readable.\n>      \n>          Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>      \n> 10:  35d12779ea1 ! 10:  ce98c61bf40 blame tests: simplify userdiff driver test\n>     @@ Commit message\n>          test_commit, 2021-01-12).\n>      \n>          We also did not need the full fortran-external-function content. Let's\n>     -    cut it down to just the important parts, and further modify it to\n>     -    demonstrate that the fortran-specific userdiff function is in effect\n>     -    by adding \"DO NOT MATCH ...\" and \"AS THE ...\" lines surrounding the\n>     -    \"RIGHT\" one. This is to check that we're using the userdiff \"fortran\"\n>     -    driver, as opposed to the default driver.\n>     +    cut it down to just the important parts.\n>      \n>     -    The test also left behind a .gitattributes files, let's clean it up\n>     -    with \"test_when_finished\".\n>     +    I'm modifying it to demonstrate that the fortran-specific userdiff\n>     +    function is in effect by adding \"DO NOT MATCH ...\" and \"AS THE ...\"\n>     +    lines surrounding the \"RIGHT\" one.\n>     +\n>     +    This is to check that we're using the userdiff \"fortran\" driver, as\n>     +    opposed to the default driver which would match on those lines as part\n>     +    of the general heuristic of matching a line that doesn't begin with\n>     +    whitespace.\n>     +\n>     +    The test had also been leaving behind a .gitattributes file for later\n>     +    tests to possibly trip over, let's clean it up with\n>     +    \"test_when_finished\".\n>      \n>          Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>      \n> \n"},{"id":"420129","messageId":"YFuJJCSGPXbrt0Mx@coredump.intra.peff.net","threadId":"55144","inReplyTo":"patch-01.11-fb7346cd296-20210324T014604Z-avarab@gmail.com","subject":"Re: [PATCH v4 01/10] userdiff: refactor away the parse_bool() function","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-03-24T18:47:00Z","receivedAt":"2021-03-24T18:48:05Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Mar 24, 2021 at 02:48:43AM +0100, Ævar Arnfjörð Bjarmason wrote:\n\n> Since 6680a0874f (drop odd return value semantics from\n> userdiff_config, 2012-02-07) we have not cared about the return values\n> of parse_tristate() or git_config_bool() v.s. falling through in\n> userdiff_config(), so let's do so in those cases to make the code\n> easier to read.\n> \n> Having a wrapper function for git_config_bool() dates back to\n> d9bae1a178 (diff: cache textconv output, 2010-04-01) and\n> 122aa6f9c0 (diff: introduce diff.<driver>.binary, 2008-10-05), both of\n> which predated the change in 6680a0874f which made their return values\n> redundant.\n\nI think this cleanup makes sense.\n\n>  int userdiff_config(const char *k, const char *v)\n> @@ -312,16 +305,17 @@ int userdiff_config(const char *k, const char *v)\n>  \t\treturn parse_funcname(&drv->funcname, k, v, 0);\n>  \tif (!strcmp(type, \"xfuncname\"))\n>  \t\treturn parse_funcname(&drv->funcname, k, v, REG_EXTENDED);\n> -\tif (!strcmp(type, \"binary\"))\n> -\t\treturn parse_tristate(&drv->binary, k, v);\n>  \tif (!strcmp(type, \"command\"))\n>  \t\treturn git_config_string(&drv->external, k, v);\n>  \tif (!strcmp(type, \"textconv\"))\n>  \t\treturn git_config_string(&drv->textconv, k, v);\n> -\tif (!strcmp(type, \"cachetextconv\"))\n> -\t\treturn parse_bool(&drv->textconv_want_cache, k, v);\n>  \tif (!strcmp(type, \"wordregex\"))\n>  \t\treturn git_config_string(&drv->word_regex, k, v);\n> +\t/* Don't care about the parse errors for these, fallthrough */\n> +\tif (!strcmp(type, \"cachetextconv\"))\n> +\t\tdrv->textconv_want_cache = git_config_bool(k, v);\n> +\tif (!strcmp(type, \"binary\"))\n> +\t\tparse_tristate(&drv->binary, k, v);\n\nThe original returned early, which short-circuited the rest of the\nstrcmp(). that probably doesn't matter much in practice (after all, an\nunrecognized value is inherently O(n)). But perhaps:\n\n  if (...)\n     assign...;\n  else if (...)\n     assign...;\n\nwould make the comment unnecessary. I don't feel strongly either way,\nthough.\n\n-Peff\n"},{"id":"420131","messageId":"xmqq5z1gz869.fsf@gitster.g","threadId":"55144","inReplyTo":"7c560336-c087-5159-06c2-5b22e949902e@kdbg.org","subject":"Re: [PATCH v4 00/10] userdiff: refactor + test improvements","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-03-24T19:02:54Z","receivedAt":"2021-03-24T19:03:54Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Sixt <j6t@kdbg.org> writes:\n\n> Am 24.03.21 um 02:48 schrieb Ævar Arnfjörð Bjarmason:\n>> This is a restart of the 35-patch v3 of this topic at\n>> https://lore.kernel.org/git/20210224195129.4004-1-avarab@gmail.com/\n>> \n>> I still plan on submitting the rest of it, but wanted to start with\n>> the early parts of that series that hasn't been controversial or has\n>> outstanding feedback I haven't addressed.\n>> \n>> The range-diff to v3 is just for those patches I'm re-rolling here.\n>\n> I'm mostly relying on the interdiff below. I think I had no comments on\n> these patches in the earlier round, so:\n>\n> Acked-by: Johannes Sixt <j6t@kdbg.org>\n\nThanks.  I've read through everything in the patch this round,\nignoring anything that came before, and them looked mostly fine.\nPeff's comment on 01/10 to make it if/else if cascade does make\nsense to me, too, though.\n\n"},{"id":"420133","messageId":"YFuPMMMYicFK6A/S@coredump.intra.peff.net","threadId":"55144","inReplyTo":"patch-05.11-64ea5e8443f-20210324T014604Z-avarab@gmail.com","subject":"Re: [PATCH v4 05/10] userdiff: add and use for_each_userdiff_driver()","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-03-24T19:12:48Z","receivedAt":"2021-03-24T19:13:31Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Mar 24, 2021 at 02:48:47AM +0100, Ævar Arnfjörð Bjarmason wrote:\n\n> Refactor the userdiff_find_by_namelen() function so that a new\n> for_each_userdiff_driver() API function does most of the work.\n> \n> This will be useful for the same reason we've got other for_each_*()\n> API functions as part of various APIs, and will be used in a follow-up\n> commit.\n\nThe refactorings up to here all made sense, but TBH this one makes the\ncode more confusing to follow to me.\n\nPerhaps part of it is just that the diff is messy, but I had to read it\nseveral times to understand what's going on. Here's what I think were\nthe tricky parts:\n\n> -static struct userdiff_driver *userdiff_find_by_namelen(const char *k, size_t len)\n> +struct for_each_userdiff_driver_cb {\n> +\tconst char *k;\n> +\tsize_t len;\n> +\tstruct userdiff_driver *driver;\n> +};\n\nOur callback function does _one_ type of selection (based on a \"type\"\nparameter), but not another (based on the name). That feels\ninconsistent, but is also the reason we have this awkward struct.  Part\nof my confusion is the name: this is not something to be generically\nused with for_each_userdiff_driver(), but rather a type unique to\nfind_by_namelen() to be passed through the opaque void pointer.\n\nSo \"struct find_by_namelen_data\" would have been a lot more\nenlightening.\n\nThe fact that callbacks are awkward in general in C might not be\nsolvable, at least not without duplicating some iteration code.\n\n> +static int userdiff_find_by_namelen_cb(struct userdiff_driver *driver,\n> +\t\t\t\t       enum userdiff_driver_type type, void *priv)\n>  {\n> [...]\n> +\tif (!strncmp(driver->name, cb_data->k, cb_data->len) &&\n> +\t    !driver->name[cb_data->len]) {\n> +\t\tcb_data->driver = driver;\n> +\t\treturn -1; /* found it! */\n>  \t}\n\nThis \"return -1\" took me a while to grok, and the comment didn't help\nall that much. The point is to stop traversing the list, but \"-1\" to me\nsignals error. I think returning \"1\" might be a bit more idiomatic, but\nalso a comment that says \"tell the caller to stop iterating\" would have\nbeen more clear.\n\n> +int for_each_userdiff_driver(each_userdiff_driver_fn fn,\n> +\t\t\t     enum userdiff_driver_type type, void *cb_data)\n> +{\n> +\tint i, ret;\n> +\tif (type & (USERDIFF_DRIVER_TYPE_UNSPECIFIED | USERDIFF_DRIVER_TYPE_CUSTOM)) {\n> +\n> +\t\tfor (i = 0; i < ndrivers; i++) {\n> +\t\t\tstruct userdiff_driver *drv = drivers + i;\n> +\t\t\tret = fn(drv, USERDIFF_DRIVER_TYPE_CUSTOM, cb_data);\n> +\t\t\tif (ret)\n> +\t\t\t\treturn ret;\n> +\t\t}\n> +\t}\n> +\tif (type & (USERDIFF_DRIVER_TYPE_UNSPECIFIED | USERDIFF_DRIVER_TYPE_BUILTIN)) {\n> +\n> +\t\tfor (i = 0; i < ARRAY_SIZE(builtin_drivers); i++) {\n> +\t\t\tstruct userdiff_driver *drv = builtin_drivers + i;\n> +\t\t\tret = fn(drv, USERDIFF_DRIVER_TYPE_BUILTIN, cb_data);\n> +\t\t\tif (ret)\n> +\t\t\t\treturn ret;\n> +\t\t}\n> +\t}\n> +\treturn 0;\n> +}\n\nI spent a while scratching my head at these types, and what they would\nbe used for, since this caller doesn't introduce any. Looking at patch 7\nhelped, though it's unclear to me why we need to distinguish between\ncustom and builtin drivers there. As you note there, nobody calls\nlist-custom-drivers nor list-drivers. And if we haven't configured\nanything, then wouldn't list-drivers be the same as list-builtin-drivers?\nOr for the purposes of that test, if we _did_ configure something,\n\n  As an aside, it feels like this is something we ought to be able to\n  ask git-config about, rather than having a test-helper. This is\n  basically \"baked-in\" config, and if we represented it as such, and\n  parsed it into a struct just like regular config, then probably \"git\n  config --list --source\" could be used to find it (and differentiate it\n  from user-provided config). Possible downsides:\n\n    1. Would people find it confusing that \"git config --list\" suddenly\n       gets way bigger? Maybe we'd want an \"--include-baked-in\" option\n       or something.\n\n    2. Is the cost of parsing the config measurably bad? Obviously a\n       user could provide the same content and we'd have to parse it,\n       but there's a lot more rules here than most users would probably\n       provide.\n\n> +enum userdiff_driver_type {\n> +\tUSERDIFF_DRIVER_TYPE_UNSPECIFIED = 1<<0,\n> +\tUSERDIFF_DRIVER_TYPE_BUILTIN = 1<<1,\n> +\tUSERDIFF_DRIVER_TYPE_CUSTOM = 1<<2,\n> +};\n\nI was confused by these being bits, because some of them seem mutually\nexclusive (e.g., UNSPECIFIED and anything else).\n\nPerhaps it would make more sense as:\n\n  USERDIFF_DRIVER_TYPE_BUILTIN = 1<<0,\n  USERDIFF_DRIVER_TYPE_CUSTOM = 1<<0,\n  USERDIFF_DRIVER_TYPE_ALL = USERDIFF_DRIVER_TYPE_BUILTIN | USERDIFF_DRIVER_TYPE_CUSTOM\n\nOr the one caller who wants \"ALL\" could even do the OR themselves.\n\nI do kind of wonder if there's much value in having a single function\nwith a type field at all, though, given that there's no overlap in the\nimplementation. Would separate \"for_each_custom\" and \"for_each_builtin\"\nfunctions make sense? And then the existing caller would just call them\nsequentially.\n\nI dunno. I know a lot of this is nit-picking, and I don't think there's\nanything incorrect in this patch. I just found it surprisingly hard to\nread for something that purports to be refactoring / cleaning the code.\n\n-Peff\n"},{"id":"420134","messageId":"YFuPrJUgAY3B0nRw@coredump.intra.peff.net","threadId":"55144","inReplyTo":"xmqq5z1gz869.fsf@gitster.g","subject":"Re: [PATCH v4 00/10] userdiff: refactor + test improvements","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-03-24T19:14:52Z","receivedAt":"2021-03-24T19:15:53Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Mar 24, 2021 at 12:02:54PM -0700, Junio C Hamano wrote:\n\n> Johannes Sixt <j6t@kdbg.org> writes:\n> \n> > Am 24.03.21 um 02:48 schrieb Ævar Arnfjörð Bjarmason:\n> >> This is a restart of the 35-patch v3 of this topic at\n> >> https://lore.kernel.org/git/20210224195129.4004-1-avarab@gmail.com/\n> >> \n> >> I still plan on submitting the rest of it, but wanted to start with\n> >> the early parts of that series that hasn't been controversial or has\n> >> outstanding feedback I haven't addressed.\n> >> \n> >> The range-diff to v3 is just for those patches I'm re-rolling here.\n> >\n> > I'm mostly relying on the interdiff below. I think I had no comments on\n> > these patches in the earlier round, so:\n> >\n> > Acked-by: Johannes Sixt <j6t@kdbg.org>\n> \n> Thanks.  I've read through everything in the patch this round,\n> ignoring anything that came before, and them looked mostly fine.\n> Peff's comment on 01/10 to make it if/else if cascade does make\n> sense to me, too, though.\n\nI left some other comments on patch 4, mostly about clarity. But just to\nbe clear, I don't think there's anything incorrect there, and I wouldn't\nbe offended if it gets picked up as-is.\n\n-Peff\n"},{"id":"420140","messageId":"xmqqsg4kxr38.fsf@gitster.g","threadId":"55144","inReplyTo":"YFuPMMMYicFK6A/S@coredump.intra.peff.net","subject":"Re: [PATCH v4 05/10] userdiff: add and use for_each_userdiff_driver()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-03-24T19:57:15Z","receivedAt":"2021-03-24T19:58:23Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> Our callback function does _one_ type of selection (based on a \"type\"\n> parameter), but not another (based on the name). That feels\n> inconsistent, but is also the reason we have this awkward struct.\n\nI wrote a very similar review but did not send it out, as I couldn't\npinpoint where the awkwardness come from exactly.\n\n> Part\n> of my confusion is the name: this is not something to be generically\n> used with for_each_userdiff_driver(), but rather a type unique to\n> find_by_namelen() to be passed through the opaque void pointer.\n>\n> So \"struct find_by_namelen_data\" would have been a lot more\n> enlightening.\n\nYes.  The callback parameter being \"void *\" is the API, and it is\njust this user that uses this particular structure.\n\nAnd while \"type\" could also be made a part of this structure and\nhave the API always iterate over all entries regardless of \"type\",\ni.e. the callback function could be the one responsible for finding\nthe entries in the table for one particular type, it is understandable\nthat potential callers can be helped by having the pre-filtering\nbased on the \"type\" thing on the API side.\n\n>> +\tif (!strncmp(driver->name, cb_data->k, cb_data->len) &&\n>> +\t    !driver->name[cb_data->len]) {\n>> +\t\tcb_data->driver = driver;\n>> +\t\treturn -1; /* found it! */\n>>  \t}\n>\n> This \"return -1\" took me a while to grok, and the comment didn't help\n> all that much. The point is to stop traversing the list, but \"-1\" to me\n> signals error. I think returning \"1\" might be a bit more idiomatic, but\n> also a comment that says \"tell the caller to stop iterating\" would have\n> been more clear.\n\nAnd the fact that it becomes the return value of \"for_each_()\" iterator\ndoes not quite help to use a negative value, implying there was some\nerror condition X-<.\n\n> Perhaps it would make more sense as:\n>\n>   USERDIFF_DRIVER_TYPE_BUILTIN = 1<<0,\n>   USERDIFF_DRIVER_TYPE_CUSTOM = 1<<0,\n>   USERDIFF_DRIVER_TYPE_ALL = USERDIFF_DRIVER_TYPE_BUILTIN | USERDIFF_DRIVER_TYPE_CUSTOM\n>\n> Or the one caller who wants \"ALL\" could even do the OR themselves.\n\nGreat minds think alike ;-)\n\n> I do kind of wonder if there's much value in having a single function\n> with a type field at all, though, given that there's no overlap in the\n> implementation. Would separate \"for_each_custom\" and \"for_each_builtin\"\n> functions make sense? And then the existing caller would just call them\n> sequentially.\n\nOr a single for_each_driver() that gives <name, length, type> tuple\nto the callback function, iterating over all drivers regardless of\nthe type.\n\n"},{"id":"420148","messageId":"YFukY9XZkO1vRYZC@coredump.intra.peff.net","threadId":"55144","inReplyTo":"xmqqsg4kxr38.fsf@gitster.g","subject":"Re: [PATCH v4 05/10] userdiff: add and use for_each_userdiff_driver()","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-03-24T20:43:15Z","receivedAt":"2021-03-24T20:44:21Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Mar 24, 2021 at 12:57:15PM -0700, Junio C Hamano wrote:\n\n> And while \"type\" could also be made a part of this structure and\n> have the API always iterate over all entries regardless of \"type\",\n> i.e. the callback function could be the one responsible for finding\n> the entries in the table for one particular type, it is understandable\n> that potential callers can be helped by having the pre-filtering\n> based on the \"type\" thing on the API side.\n\nYeah. We _could_ also have the for-each function filter by name (if\npresent). And then quit when it finds a matching name (because it knows\nthe names are supposed to be unique).\n\nThat is the opposite of:\n\n> Or a single for_each_driver() that gives <name, length, type> tuple\n> to the callback function, iterating over all drivers regardless of\n> the type.\n\nI'd be fine with that, too. I don't have a huge preference either way,\nbut it does feel like it would fix the weird asymmetry (though as I\nsaid, I don't think the asymmetry is _wrong_, and it may not be worth\nover-engineering this tiny corner of the interface).\n\nThanks for spelling out both of these directions clearly. My response to\nÆvar was a little muddled as I was having trouble figuring out just what\nit was that left me so puzzled by the patch. :)\n\n\nBy the way, one thing I wondered about all of this: could we have an\nentry in both the custom and builtin lists? I think the answer is \"no\",\nbecause any custom config for a builtin type would get placed inside the\nexisting struct in the builtin_drivers array (which is sadly a reason we\nmust leak any old string values when we parse diff.*.funcname, etc; we\ndon't know if they are string literals or heap values from previously\nparsed config).\n\nI also notice that the \"builtin\" for-each does not count the boolean\n\"diff\" or \"!diff\" attribute structs. That is perhaps reasonable, as they\ndo not have interesting names nor values in the first place. I do still\nwonder whether \"builtin vs custom\" is even a useful distinction (i.e.,\nthose builtins are less builtin_drivers[] than drivers[]).\n\n-Peff\n"},{"id":"420164","messageId":"87im5gp2ze.fsf@evledraar.gmail.com","threadId":"55144","inReplyTo":"YFuPMMMYicFK6A/S@coredump.intra.peff.net","subject":"Re: [PATCH v4 05/10] userdiff: add and use for_each_userdiff_driver()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-24T23:05:09Z","receivedAt":"2021-03-24T23:05:56Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Mar 24 2021, Jeff King wrote:\n\n> On Wed, Mar 24, 2021 at 02:48:47AM +0100, Ævar Arnfjörð Bjarmason wrote:\n>\n>> Refactor the userdiff_find_by_namelen() function so that a new\n>> for_each_userdiff_driver() API function does most of the work.\n>> \n>> This will be useful for the same reason we've got other for_each_*()\n>> API functions as part of various APIs, and will be used in a follow-up\n>> commit.\n>\n> The refactorings up to here all made sense, but TBH this one makes the\n> code more confusing to follow to me.\n>\n> Perhaps part of it is just that the diff is messy, but I had to read it\n> several times to understand what's going on. Here's what I think were\n> the tricky parts:\n>\n>> -static struct userdiff_driver *userdiff_find_by_namelen(const char *k, size_t len)\n>> +struct for_each_userdiff_driver_cb {\n>> +\tconst char *k;\n>> +\tsize_t len;\n>> +\tstruct userdiff_driver *driver;\n>> +};\n>\n> Our callback function does _one_ type of selection (based on a \"type\"\n> parameter), but not another (based on the name). That feels\n> inconsistent, but is also the reason we have this awkward struct.  Part\n> of my confusion is the name: this is not something to be generically\n> used with for_each_userdiff_driver(), but rather a type unique to\n> find_by_namelen() to be passed through the opaque void pointer.\n>\n> So \"struct find_by_namelen_data\" would have been a lot more\n> enlightening.\n>\n> The fact that callbacks are awkward in general in C might not be\n> solvable, at least not without duplicating some iteration code.\n>\n>> +static int userdiff_find_by_namelen_cb(struct userdiff_driver *driver,\n>> +\t\t\t\t       enum userdiff_driver_type type, void *priv)\n>>  {\n>> [...]\n>> +\tif (!strncmp(driver->name, cb_data->k, cb_data->len) &&\n>> +\t    !driver->name[cb_data->len]) {\n>> +\t\tcb_data->driver = driver;\n>> +\t\treturn -1; /* found it! */\n>>  \t}\n>\n> This \"return -1\" took me a while to grok, and the comment didn't help\n> all that much. The point is to stop traversing the list, but \"-1\" to me\n> signals error. I think returning \"1\" might be a bit more idiomatic, but\n> also a comment that says \"tell the caller to stop iterating\" would have\n> been more clear.\n\n*nod*\n\nAlso thanks for all the reviewing so far both, I'm not replying to all\nof it point-by-point here, will respond with a re-roll at some point.\n\n>> +int for_each_userdiff_driver(each_userdiff_driver_fn fn,\n>> +\t\t\t     enum userdiff_driver_type type, void *cb_data)\n>> +{\n>> +\tint i, ret;\n>> +\tif (type & (USERDIFF_DRIVER_TYPE_UNSPECIFIED | USERDIFF_DRIVER_TYPE_CUSTOM)) {\n>> +\n>> +\t\tfor (i = 0; i < ndrivers; i++) {\n>> +\t\t\tstruct userdiff_driver *drv = drivers + i;\n>> +\t\t\tret = fn(drv, USERDIFF_DRIVER_TYPE_CUSTOM, cb_data);\n>> +\t\t\tif (ret)\n>> +\t\t\t\treturn ret;\n>> +\t\t}\n>> +\t}\n>> +\tif (type & (USERDIFF_DRIVER_TYPE_UNSPECIFIED | USERDIFF_DRIVER_TYPE_BUILTIN)) {\n>> +\n>> +\t\tfor (i = 0; i < ARRAY_SIZE(builtin_drivers); i++) {\n>> +\t\t\tstruct userdiff_driver *drv = builtin_drivers + i;\n>> +\t\t\tret = fn(drv, USERDIFF_DRIVER_TYPE_BUILTIN, cb_data);\n>> +\t\t\tif (ret)\n>> +\t\t\t\treturn ret;\n>> +\t\t}\n>> +\t}\n>> +\treturn 0;\n>> +}\n>\n> I spent a while scratching my head at these types, and what they would\n> be used for, since this caller doesn't introduce any. Looking at patch 7\n> helped, though it's unclear to me why we need to distinguish between\n> custom and builtin drivers there. As you note there, nobody calls\n> list-custom-drivers nor list-drivers. And if we haven't configured\n> anything, then wouldn't list-drivers be the same as list-builtin-drivers?\n> Or for the purposes of that test, if we _did_ configure something,\n>\n>   As an aside, it feels like this is something we ought to be able to\n>   ask git-config about, rather than having a test-helper. This is\n>   basically \"baked-in\" config, and if we represented it as such, and\n>   parsed it into a struct just like regular config, then probably \"git\n>   config --list --source\" could be used to find it (and differentiate it\n>   from user-provided config). Possible downsides:\n>\n>     1. Would people find it confusing that \"git config --list\" suddenly\n>        gets way bigger? Maybe we'd want an \"--include-baked-in\" option\n>        or something.\n>\n>     2. Is the cost of parsing the config measurably bad? Obviously a\n>        user could provide the same content and we'd have to parse it,\n>        but there's a lot more rules here than most users would probably\n>        provide.\n\nAlso:\n\n 3. Only the PATTERNS() macro translates as-is to config syntax. We\n    don't have a way to do what IPATTERN() does in the config syntax\n    currently.\n\n    We could add a ifuncname and xifuncname or whatever for it I guess,\n    but currently the ICASE behavior in the C code is magic.\n\n>> +enum userdiff_driver_type {\n>> +\tUSERDIFF_DRIVER_TYPE_UNSPECIFIED = 1<<0,\n>> +\tUSERDIFF_DRIVER_TYPE_BUILTIN = 1<<1,\n>> +\tUSERDIFF_DRIVER_TYPE_CUSTOM = 1<<2,\n>> +};\n>\n> I was confused by these being bits, because some of them seem mutually\n> exclusive (e.g., UNSPECIFIED and anything else).\n>\n> Perhaps it would make more sense as:\n>\n>   USERDIFF_DRIVER_TYPE_BUILTIN = 1<<0,\n>   USERDIFF_DRIVER_TYPE_CUSTOM = 1<<0,\n>   USERDIFF_DRIVER_TYPE_ALL = USERDIFF_DRIVER_TYPE_BUILTIN | USERDIFF_DRIVER_TYPE_CUSTOM\n>\n> Or the one caller who wants \"ALL\" could even do the OR themselves.\n>\n> I do kind of wonder if there's much value in having a single function\n> with a type field at all, though, given that there's no overlap in the\n> implementation. Would separate \"for_each_custom\" and \"for_each_builtin\"\n> functions make sense? And then the existing caller would just call them\n> sequentially.\n>\n> I dunno. I know a lot of this is nit-picking, and I don't think there's\n> anything incorrect in this patch. I just found it surprisingly hard to\n> read for something that purports to be refactoring / cleaning the code.\n>\n> -Peff\n\n"},{"id":"420167","messageId":"YFvaYokQJpvwSzJ6@coredump.intra.peff.net","threadId":"55144","inReplyTo":"87im5gp2ze.fsf@evledraar.gmail.com","subject":"Re: [PATCH v4 05/10] userdiff: add and use for_each_userdiff_driver()","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-03-25T00:33:38Z","receivedAt":"2021-03-25T00:34:39Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Mar 25, 2021 at 12:05:09AM +0100, Ævar Arnfjörð Bjarmason wrote:\n\n> >   As an aside, it feels like this is something we ought to be able to\n> >   ask git-config about, rather than having a test-helper. This is\n> >   basically \"baked-in\" config, and if we represented it as such, and\n> >   parsed it into a struct just like regular config, then probably \"git\n> >   config --list --source\" could be used to find it (and differentiate it\n> >   from user-provided config). Possible downsides:\n> >\n> >     1. Would people find it confusing that \"git config --list\" suddenly\n> >        gets way bigger? Maybe we'd want an \"--include-baked-in\" option\n> >        or something.\n> >\n> >     2. Is the cost of parsing the config measurably bad? Obviously a\n> >        user could provide the same content and we'd have to parse it,\n> >        but there's a lot more rules here than most users would probably\n> >        provide.\n> \n> Also:\n> \n>  3. Only the PATTERNS() macro translates as-is to config syntax. We\n>     don't have a way to do what IPATTERN() does in the config syntax\n>     currently.\n> \n>     We could add a ifuncname and xifuncname or whatever for it I guess,\n>     but currently the ICASE behavior in the C code is magic.\n\nGood point. IMHO that is something we should consider fixing\nindependently. It was a mistake to add builtins that couldn't be\nreplicated via the config (though I notice it happened quite a while\nago, and nobody seems to have cared, so perhaps it isn't that\nimportant).\n\nI have also wondered if we should just ship a file which could be\ninstalled as /etc/gitconfig.filetypes and include it the stock\n/etc/gitconfig. That is effectively the same as \"baked in\", but\nhopefully makes it more clear to users how they can modify things.  But\nall of that is somewhat orthogonal to what you're doing here.\n\n-Peff\n"},{"id":"420170","messageId":"87czvoowg2.fsf@evledraar.gmail.com","threadId":"55144","inReplyTo":"YFvaYokQJpvwSzJ6@coredump.intra.peff.net","subject":"Re: [PATCH v4 05/10] userdiff: add and use for_each_userdiff_driver()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-25T01:26:21Z","receivedAt":"2021-03-25T01:27:06Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Mar 25 2021, Jeff King wrote:\n\n> On Thu, Mar 25, 2021 at 12:05:09AM +0100, Ævar Arnfjörð Bjarmason wrote:\n>\n>> >   As an aside, it feels like this is something we ought to be able to\n>> >   ask git-config about, rather than having a test-helper. This is\n>> >   basically \"baked-in\" config, and if we represented it as such, and\n>> >   parsed it into a struct just like regular config, then probably \"git\n>> >   config --list --source\" could be used to find it (and differentiate it\n>> >   from user-provided config). Possible downsides:\n>> >\n>> >     1. Would people find it confusing that \"git config --list\" suddenly\n>> >        gets way bigger? Maybe we'd want an \"--include-baked-in\" option\n>> >        or something.\n>> >\n>> >     2. Is the cost of parsing the config measurably bad? Obviously a\n>> >        user could provide the same content and we'd have to parse it,\n>> >        but there's a lot more rules here than most users would probably\n>> >        provide.\n>> \n>> Also:\n>> \n>>  3. Only the PATTERNS() macro translates as-is to config syntax. We\n>>     don't have a way to do what IPATTERN() does in the config syntax\n>>     currently.\n>> \n>>     We could add a ifuncname and xifuncname or whatever for it I guess,\n>>     but currently the ICASE behavior in the C code is magic.\n>\n> Good point. IMHO that is something we should consider fixing\n> independently. It was a mistake to add builtins that couldn't be\n> replicated via the config (though I notice it happened quite a while\n> ago, and nobody seems to have cared, so perhaps it isn't that\n> important).\n\nI'm conspiring to eventually optimistically replace these ERE patterns\nwith PCRE if we build with that at some point.\n\nThen you could just prefix your pattern with (?i) here in this and other\nthings that want icase...\n\n> I have also wondered if we should just ship a file which could be\n> installed as /etc/gitconfig.filetypes and include it the stock\n> /etc/gitconfig. That is effectively the same as \"baked in\", but\n> hopefully makes it more clear to users how they can modify things.  But\n> all of that is somewhat orthogonal to what you're doing here.\n\nIn theory I'm with you on that, in practice this is just the sort of\nthing that requires opt-in effort from every person packaging git\n(installing system-wide-config).\n\nA good number of those people will decide \"default config values? In\nsome ~200 line file included in /etc/gitconfig?? I don't need that! This\nis a minimal install!\".\n\nAnd then their web UI that calls \"git diff\" under the hood won't do the\nright thing with a .gitattributes config that expects these built-in\ndrivers anymore.\n\nBut yeah, there's no reason your earlier suggestion of injecting global\ndefaults into \"git config -l\" wouldn't work, and for our own C APIs such\nas this one to rely on that happening.\n\nIt would even make git more discoverable, as all the \"this is set to xyz\nby default\" docs in git-config(1) could become reflective, you could\njust run \"git config -l --show-origin | grep ^default:\" or something to\nsee all default values.\n"},{"id":"420225","messageId":"YF1UuNBrAWvHSCp1@coredump.intra.peff.net","threadId":"55144","inReplyTo":"87czvoowg2.fsf@evledraar.gmail.com","subject":"Re: [PATCH v4 05/10] userdiff: add and use for_each_userdiff_driver()","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-03-26T03:27:52Z","receivedAt":"2021-03-26T03:28:57Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Mar 25, 2021 at 02:26:21AM +0100, Ævar Arnfjörð Bjarmason wrote:\n\n> >>     We could add a ifuncname and xifuncname or whatever for it I guess,\n> >>     but currently the ICASE behavior in the C code is magic.\n> >\n> > Good point. IMHO that is something we should consider fixing\n> > independently. It was a mistake to add builtins that couldn't be\n> > replicated via the config (though I notice it happened quite a while\n> > ago, and nobody seems to have cared, so perhaps it isn't that\n> > important).\n> \n> I'm conspiring to eventually optimistically replace these ERE patterns\n> with PCRE if we build with that at some point.\n> \n> Then you could just prefix your pattern with (?i) here in this and other\n> things that want icase...\n\nI really like PCRE myself, but is it portable/common enough for us to\nstart using it for baked-in funcname patterns? I sort of assumed there\nwere exotic platforms where libpcre wouldn't build (or at least it would\nbe inconvenient or uncommon to have it). And it would be nice to degrade\nthose gracefully (or at least better than \"I guess you don't get any\nbuiltin funcnames. Tough luck\").\n\n> > I have also wondered if we should just ship a file which could be\n> > installed as /etc/gitconfig.filetypes and include it the stock\n> > /etc/gitconfig. That is effectively the same as \"baked in\", but\n> > hopefully makes it more clear to users how they can modify things.  But\n> > all of that is somewhat orthogonal to what you're doing here.\n> \n> In theory I'm with you on that, in practice this is just the sort of\n> thing that requires opt-in effort from every person packaging git\n> (installing system-wide-config).\n\nI assumed that we'd install it with \"make install\", and packaging would\npick it up from there. But you're right that it is likely to create extra\nheadaches.\n\n> But yeah, there's no reason your earlier suggestion of injecting global\n> defaults into \"git config -l\" wouldn't work, and for our own C APIs such\n> as this one to rely on that happening.\n> \n> It would even make git more discoverable, as all the \"this is set to xyz\n> by default\" docs in git-config(1) could become reflective, you could\n> just run \"git config -l --show-origin | grep ^default:\" or something to\n> see all default values.\n\nYeah, exactly. The discoverability is the real value IMHO. I just worry\nabout overwhelming the user who runs \"git config\" with --show-origin. I\nguess it could avoid showing baked-in config unless an extra option is\ngiven, but then that makes discoverability slightly harder (though still\nbetter than it is today).\n\nI dunno. This is mostly orthogonal to your patch series, so I don't mind\nat all punting on it for now. Though if we _did_ do the baked-in config\nthen, then a lot of this userdiff code would want to be converted to it.\n\n-Peff\n"},{"id":"421225","messageId":"patch-01.10-2f7a7b2897-20210408T145833Z-avarab@gmail.com","threadId":"55144","inReplyTo":"cover-00.10-0000000000-20210408T145833Z-avarab@gmail.com","subject":"[PATCH v5 1/9] userdiff style: re-order drivers in alphabetical order","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-08T15:04:16Z","receivedAt":"2021-04-08T15:04:39Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Address some old code smell and move around the built-in userdiff\ndrivers so they're both in alphabetical order, and now in the same\norder they appear in the gitattributes(5) documentation.\n\nThe two started drifting in be58e70dba (diff: unify external diff and\nfuncname parsing code, 2008-10-05), and then even further in\n80c49c3de2 (color-words: make regex configurable via attributes,\n2009-01-17) when the \"cpp\" pattern was added.\n\nThere are no functional changes here, and as --color-moved will show\nonly moved existing lines.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n userdiff.c | 76 +++++++++++++++++++++++++++---------------------------\n 1 file changed, 38 insertions(+), 38 deletions(-)\n\ndiff --git a/userdiff.c b/userdiff.c\nindex 3f81a2261c..650f421d63 100644\n--- a/userdiff.c\n+++ b/userdiff.c\n@@ -44,6 +44,44 @@ PATTERNS(\"bash\",\n \t /* -- */\n \t /* Characters not in the default $IFS value */\n \t \"[^ \\t]+\"),\n+PATTERNS(\"bibtex\", \"(@[a-zA-Z]{1,}[ \\t]*\\\\{{0,1}[ \\t]*[^ \\t\\\"@',\\\\#}{~%]*).*$\",\n+\t \"[={}\\\"]|[^={}\\\" \\t]+\"),\n+PATTERNS(\"cpp\",\n+\t /* Jump targets or access declarations */\n+\t \"!^[ \\t]*[A-Za-z_][A-Za-z_0-9]*:[[:space:]]*($|/[/*])\\n\"\n+\t /* functions/methods, variables, and compounds at top level */\n+\t \"^((::[[:space:]]*)?[A-Za-z_].*)$\",\n+\t /* -- */\n+\t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n+\t \"|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lLuU]*\"\n+\t \"|[-+*/<>%&^|=!]=|--|\\\\+\\\\+|<<=?|>>=?|&&|\\\\|\\\\||::|->\\\\*?|\\\\.\\\\*\"),\n+PATTERNS(\"csharp\",\n+\t /* Keywords */\n+\t \"!^[ \\t]*(do|while|for|if|else|instanceof|new|return|switch|case|throw|catch|using)\\n\"\n+\t /* Methods and constructors */\n+\t \"^[ \\t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe|async)[ \\t]+)*[][<>@.~_[:alnum:]]+[ \\t]+[<>@._[:alnum:]]+[ \\t]*\\\\(.*\\\\))[ \\t]*$\\n\"\n+\t /* Properties */\n+\t \"^[ \\t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \\t]+)*[][<>@.~_[:alnum:]]+[ \\t]+[@._[:alnum:]]+)[ \\t]*$\\n\"\n+\t /* Type definitions */\n+\t \"^[ \\t]*(((static|public|internal|private|protected|new|unsafe|sealed|abstract|partial)[ \\t]+)*(class|enum|interface|struct)[ \\t]+.*)$\\n\"\n+\t /* Namespace */\n+\t \"^[ \\t]*(namespace[ \\t]+.*)$\",\n+\t /* -- */\n+\t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n+\t \"|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?\"\n+\t \"|[-+*/<>%&^|=!]=|--|\\\\+\\\\+|<<=?|>>=?|&&|\\\\|\\\\||::|->\"),\n+IPATTERN(\"css\",\n+\t \"![:;][[:space:]]*$\\n\"\n+\t \"^[:[@.#]?[_a-z0-9].*$\",\n+\t /* -- */\n+\t /*\n+\t  * This regex comes from W3C CSS specs. Should theoretically also\n+\t  * allow ISO 10646 characters U+00A0 and higher,\n+\t  * but they are not handled in this regex.\n+\t  */\n+\t \"-?[_a-zA-Z][-_a-zA-Z0-9]*\" /* identifiers */\n+\t \"|-?[0-9]+|\\\\#[0-9a-fA-F]+\" /* numbers */\n+),\n PATTERNS(\"dts\",\n \t \"!;\\n\"\n \t \"!=\\n\"\n@@ -191,46 +229,8 @@ PATTERNS(\"rust\",\n \t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n \t \"|[0-9][0-9_a-fA-Fiosuxz]*(\\\\.([0-9]*[eE][+-]?)?[0-9_fF]*)?\"\n \t \"|[-+*\\\\/<>%&^|=!:]=|<<=?|>>=?|&&|\\\\|\\\\||->|=>|\\\\.{2}=|\\\\.{3}|::\"),\n-PATTERNS(\"bibtex\", \"(@[a-zA-Z]{1,}[ \\t]*\\\\{{0,1}[ \\t]*[^ \\t\\\"@',\\\\#}{~%]*).*$\",\n-\t \"[={}\\\"]|[^={}\\\" \\t]+\"),\n PATTERNS(\"tex\", \"^(\\\\\\\\((sub)*section|chapter|part)\\\\*{0,1}\\\\{.*)$\",\n \t \"\\\\\\\\[a-zA-Z@]+|\\\\\\\\.|[a-zA-Z0-9\\x80-\\xff]+\"),\n-PATTERNS(\"cpp\",\n-\t /* Jump targets or access declarations */\n-\t \"!^[ \\t]*[A-Za-z_][A-Za-z_0-9]*:[[:space:]]*($|/[/*])\\n\"\n-\t /* functions/methods, variables, and compounds at top level */\n-\t \"^((::[[:space:]]*)?[A-Za-z_].*)$\",\n-\t /* -- */\n-\t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n-\t \"|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lLuU]*\"\n-\t \"|[-+*/<>%&^|=!]=|--|\\\\+\\\\+|<<=?|>>=?|&&|\\\\|\\\\||::|->\\\\*?|\\\\.\\\\*\"),\n-PATTERNS(\"csharp\",\n-\t /* Keywords */\n-\t \"!^[ \\t]*(do|while|for|if|else|instanceof|new|return|switch|case|throw|catch|using)\\n\"\n-\t /* Methods and constructors */\n-\t \"^[ \\t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe|async)[ \\t]+)*[][<>@.~_[:alnum:]]+[ \\t]+[<>@._[:alnum:]]+[ \\t]*\\\\(.*\\\\))[ \\t]*$\\n\"\n-\t /* Properties */\n-\t \"^[ \\t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \\t]+)*[][<>@.~_[:alnum:]]+[ \\t]+[@._[:alnum:]]+)[ \\t]*$\\n\"\n-\t /* Type definitions */\n-\t \"^[ \\t]*(((static|public|internal|private|protected|new|unsafe|sealed|abstract|partial)[ \\t]+)*(class|enum|interface|struct)[ \\t]+.*)$\\n\"\n-\t /* Namespace */\n-\t \"^[ \\t]*(namespace[ \\t]+.*)$\",\n-\t /* -- */\n-\t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n-\t \"|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?\"\n-\t \"|[-+*/<>%&^|=!]=|--|\\\\+\\\\+|<<=?|>>=?|&&|\\\\|\\\\||::|->\"),\n-IPATTERN(\"css\",\n-\t \"![:;][[:space:]]*$\\n\"\n-\t \"^[:[@.#]?[_a-z0-9].*$\",\n-\t /* -- */\n-\t /*\n-\t  * This regex comes from W3C CSS specs. Should theoretically also\n-\t  * allow ISO 10646 characters U+00A0 and higher,\n-\t  * but they are not handled in this regex.\n-\t  */\n-\t \"-?[_a-zA-Z][-_a-zA-Z0-9]*\" /* identifiers */\n-\t \"|-?[0-9]+|\\\\#[0-9a-fA-F]+\" /* numbers */\n-),\n { \"default\", NULL, -1, { NULL, 0 } },\n };\n #undef PATTERNS\n-- \n2.31.1.527.g9b8f7de2547\n\n"},{"id":"421226","messageId":"patch-02.10-22e48f33b6-20210408T145833Z-avarab@gmail.com","threadId":"55144","inReplyTo":"cover-00.10-0000000000-20210408T145833Z-avarab@gmail.com","subject":"[PATCH v5 2/9] userdiff style: declare patterns with consistent style","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-08T15:04:17Z","receivedAt":"2021-04-08T15:04:41Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change those patterns which were declared with a regex on the same\nline as the \"PATTERNS()\" line to put that regex on the next line, and\nadd missing \"/* -- */\" separator comments between the pattern and\nword_regex.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n userdiff.c | 20 +++++++++++++++-----\n 1 file changed, 15 insertions(+), 5 deletions(-)\n\ndiff --git a/userdiff.c b/userdiff.c\nindex 650f421d63..33b0ce4020 100644\n--- a/userdiff.c\n+++ b/userdiff.c\n@@ -44,7 +44,9 @@ PATTERNS(\"bash\",\n \t /* -- */\n \t /* Characters not in the default $IFS value */\n \t \"[^ \\t]+\"),\n-PATTERNS(\"bibtex\", \"(@[a-zA-Z]{1,}[ \\t]*\\\\{{0,1}[ \\t]*[^ \\t\\\"@',\\\\#}{~%]*).*$\",\n+PATTERNS(\"bibtex\",\n+\t \"(@[a-zA-Z]{1,}[ \\t]*\\\\{{0,1}[ \\t]*[^ \\t\\\"@',\\\\#}{~%]*).*$\",\n+\t /* -- */\n \t \"[={}\\\"]|[^={}\\\" \\t]+\"),\n PATTERNS(\"cpp\",\n \t /* Jump targets or access declarations */\n@@ -121,7 +123,9 @@ IPATTERN(\"fortran\",\n \t  * they would have been matched above as a variable anyway. */\n \t \"|[-+]?[0-9.]+([AaIiDdEeFfLlTtXx][Ss]?[-+]?[0-9.]*)?(_[a-zA-Z0-9][a-zA-Z0-9_]*)?\"\n \t \"|//|\\\\*\\\\*|::|[/<>=]=\"),\n-IPATTERN(\"fountain\", \"^((\\\\.[^.]|(int|ext|est|int\\\\.?/ext|i/e)[. ]).*)$\",\n+IPATTERN(\"fountain\",\n+\t \"^((\\\\.[^.]|(int|ext|est|int\\\\.?/ext|i/e)[. ]).*)$\",\n+\t /* -- */\n \t \"[^ \\t-]+\"),\n PATTERNS(\"golang\",\n \t /* Functions */\n@@ -132,7 +136,9 @@ PATTERNS(\"golang\",\n \t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n \t \"|[-+0-9.eE]+i?|0[xX]?[0-9a-fA-F]+i?\"\n \t \"|[-+*/<>%&^|=!:]=|--|\\\\+\\\\+|<<=?|>>=?|&\\\\^=?|&&|\\\\|\\\\||<-|\\\\.{3}\"),\n-PATTERNS(\"html\", \"^[ \\t]*(<[Hh][1-6]([ \\t].*)?>.*)$\",\n+PATTERNS(\"html\",\n+\t \"^[ \\t]*(<[Hh][1-6]([ \\t].*)?>.*)$\",\n+\t /* -- */\n \t \"[^<>= \\t]+\"),\n PATTERNS(\"java\",\n \t \"!^[ \\t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)\\n\"\n@@ -144,6 +150,7 @@ PATTERNS(\"java\",\n \t \"|--|\\\\+\\\\+|<<=?|>>>?=?|&&|\\\\|\\\\|\"),\n PATTERNS(\"markdown\",\n \t \"^ {0,3}#{1,6}[ \\t].*\",\n+\t /* -- */\n \t \"[^<>= \\t]+\"),\n PATTERNS(\"matlab\",\n \t /*\n@@ -152,6 +159,7 @@ PATTERNS(\"matlab\",\n \t  * that is understood by both.\n \t  */\n \t \"^[[:space:]]*((classdef|function)[[:space:]].*)$|^(%%%?|##)[[:space:]].*$\",\n+\t /* -- */\n \t \"[a-zA-Z_][a-zA-Z0-9_]*|[-+0-9.e]+|[=~<>]=|\\\\.[*/\\\\^']|\\\\|\\\\||&&\"),\n PATTERNS(\"objc\",\n \t /* Negate C statements that can look like functions */\n@@ -212,13 +220,15 @@ PATTERNS(\"php\",\n \t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n \t \"|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+\"\n \t \"|[-+*/<>%&^|=!.]=|--|\\\\+\\\\+|<<=?|>>=?|===|&&|\\\\|\\\\||::|->\"),\n-PATTERNS(\"python\", \"^[ \\t]*((class|(async[ \\t]+)?def)[ \\t].*)$\",\n+PATTERNS(\"python\",\n+\t \"^[ \\t]*((class|(async[ \\t]+)?def)[ \\t].*)$\",\n \t /* -- */\n \t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n \t \"|[-+0-9.e]+[jJlL]?|0[xX]?[0-9a-fA-F]+[lL]?\"\n \t \"|[-+*/<>%&^|=!]=|//=?|<<=?|>>=?|\\\\*\\\\*=?\"),\n \t /* -- */\n-PATTERNS(\"ruby\", \"^[ \\t]*((class|module|def)[ \\t].*)$\",\n+PATTERNS(\"ruby\",\n+\t \"^[ \\t]*((class|module|def)[ \\t].*)$\",\n \t /* -- */\n \t \"(@|@@|\\\\$)?[a-zA-Z_][a-zA-Z0-9_]*\"\n \t \"|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+|\\\\?(\\\\\\\\C-)?(\\\\\\\\M-)?.\"\n-- \n2.31.1.527.g9b8f7de2547\n\n"},{"id":"421227","messageId":"cover-00.10-0000000000-20210408T145833Z-avarab@gmail.com","threadId":"55144","inReplyTo":"cover-00.11-00000000000-20210324T014604Z-avarab@gmail.com","subject":"[PATCH v5 0/9] userdiff: refactor + test improvements","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-08T15:04:15Z","receivedAt":"2021-04-08T15:04:42Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"As noted in v3[1] this is the start of some wider userdiff test\nimprovements.\n\nSince v3 I've ejected the parse_bool() change at the start of the\nseries. I split that into another series[2]:\n\nThis one applies on master, and doesn't conflict with the tiny\nunrelated change in userdiff.c done in that series either textually or\nsemantically.\n\nChanges since v3:\n\n * Jeff and Junio suggested refactoring the userdiff.c callback\n   facility. I've done that using some union of their suggetions, I\n   think it's easier to read this time around.\n\n   Basically, the \"for each userdiff\" callback doesn't take a \"I want\n   this type\", that's now handled in the user callback, which\n   simplifies things a lot.\n\n * Other minor changes related to the above, e.g. renaming the CB\n   struct per Jeff's suggestion.\n\n * I found that the \"list-custom-drivers\" I'd added for completeness\n   to the helper didn't work, I needed to setup the .git dir and\n   config in the helper. That's now done, and the tests check that the\n   helper does the right thing.\n\n1. https://lore.kernel.org/git/20210224195129.4004-1-avarab@gmail.com/\n2. https://lore.kernel.org/git/cover-0.6-0000000000-20210408T133125Z-avarab@gmail.com/\n\nÆvar Arnfjörð Bjarmason (9):\n  userdiff style: re-order drivers in alphabetical order\n  userdiff style: declare patterns with consistent style\n  userdiff style: normalize pascal regex declaration\n  userdiff: add and use for_each_userdiff_driver()\n  userdiff tests: explicitly test \"default\" pattern\n  userdiff tests: list builtin drivers via test-tool\n  userdiff: remove support for \"broken\" tests\n  blame tests: don't rely on t/t4018/ directory\n  blame tests: simplify userdiff driver test\n\n Makefile                 |   1 +\n t/annotate-tests.sh      |  34 ++++----\n t/helper/test-tool.c     |   1 +\n t/helper/test-tool.h     |   1 +\n t/helper/test-userdiff.c |  46 +++++++++++\n t/t4018-diff-funcname.sh |  53 +++++-------\n t/t4018/README           |   3 -\n userdiff.c               | 169 ++++++++++++++++++++++++++-------------\n userdiff.h               |  13 +++\n 9 files changed, 213 insertions(+), 108 deletions(-)\n create mode 100644 t/helper/test-userdiff.c\n\nRange-diff:\n 1:  fb7346cd29 <  -:  ---------- userdiff: refactor away the parse_bool() function\n 2:  149387155b =  1:  2f7a7b2897 userdiff style: re-order drivers in alphabetical order\n 3:  faf1a824f0 =  2:  22e48f33b6 userdiff style: declare patterns with consistent style\n 4:  1e9ddcd1a9 =  3:  dff2aa0d38 userdiff style: normalize pascal regex declaration\n 5:  64ea5e8443 !  4:  1a3a61e389 userdiff: add and use for_each_userdiff_driver()\n    @@ userdiff.c: static struct userdiff_driver driver_false = {\n      };\n      \n     -static struct userdiff_driver *userdiff_find_by_namelen(const char *k, size_t len)\n    -+struct for_each_userdiff_driver_cb {\n    -+\tconst char *k;\n    ++struct find_by_namelen_data {\n    ++\tconst char *name;\n     +\tsize_t len;\n     +\tstruct userdiff_driver *driver;\n     +};\n    @@ userdiff.c: static struct userdiff_driver driver_false = {\n     -\t\tstruct userdiff_driver *drv = builtin_drivers + i;\n     -\t\tif (!strncmp(drv->name, k, len) && !drv->name[len])\n     -\t\t\treturn drv;\n    -+\tstruct for_each_userdiff_driver_cb *cb_data = priv;\n    ++\tstruct find_by_namelen_data *cb_data = priv;\n     +\n    -+\tif (!strncmp(driver->name, cb_data->k, cb_data->len) &&\n    ++\tif (!strncmp(driver->name, cb_data->name, cb_data->len) &&\n     +\t    !driver->name[cb_data->len]) {\n     +\t\tcb_data->driver = driver;\n    -+\t\treturn -1; /* found it! */\n    ++\t\treturn 1; /* tell the caller to stop iterating */\n      \t}\n     -\treturn NULL;\n     +\treturn 0;\n     +}\n     +\n    -+static struct userdiff_driver *userdiff_find_by_namelen(const char *k, size_t len)\n    ++static struct userdiff_driver *userdiff_find_by_namelen(const char *name, size_t len)\n     +{\n    -+\tstruct for_each_userdiff_driver_cb udcbdata = { .k = k, .len = len, .driver = NULL };\n    -+\n    -+\tfor_each_userdiff_driver(userdiff_find_by_namelen_cb,\n    -+\t\t\t\t USERDIFF_DRIVER_TYPE_UNSPECIFIED, &udcbdata);\n    ++\tstruct find_by_namelen_data udcbdata = {\n    ++\t\t.name = name,\n    ++\t\t.len = len,\n    ++\t};\n    ++\tfor_each_userdiff_driver(userdiff_find_by_namelen_cb, &udcbdata);\n     +\treturn udcbdata.driver;\n      }\n      \n    @@ userdiff.c: struct userdiff_driver *userdiff_get_textconv(struct repository *r,\n      \treturn driver;\n      }\n     +\n    -+int for_each_userdiff_driver(each_userdiff_driver_fn fn,\n    -+\t\t\t     enum userdiff_driver_type type, void *cb_data)\n    ++static int for_each_userdiff_driver_list(each_userdiff_driver_fn fn,\n    ++\t\t\t\t\t enum userdiff_driver_type type, void *cb_data,\n    ++\t\t\t\t\t struct userdiff_driver *drv,\n    ++\t\t\t\t\t int drv_size)\n     +{\n    -+\tint i, ret;\n    -+\tif (type & (USERDIFF_DRIVER_TYPE_UNSPECIFIED | USERDIFF_DRIVER_TYPE_CUSTOM)) {\n    -+\n    -+\t\tfor (i = 0; i < ndrivers; i++) {\n    -+\t\t\tstruct userdiff_driver *drv = drivers + i;\n    -+\t\t\tret = fn(drv, USERDIFF_DRIVER_TYPE_CUSTOM, cb_data);\n    -+\t\t\tif (ret)\n    -+\t\t\t\treturn ret;\n    -+\t\t}\n    ++\tint i;\n    ++\tint ret;\n    ++\tfor (i = 0; i < drv_size; i++) {\n    ++\t\tstruct userdiff_driver *item = drv + i;\n    ++\t\tif ((ret = fn(item, type, cb_data)))\n    ++\t\t\treturn ret;\n     +\t}\n    -+\tif (type & (USERDIFF_DRIVER_TYPE_UNSPECIFIED | USERDIFF_DRIVER_TYPE_BUILTIN)) {\n    ++\treturn 0;\n    ++}\n    ++\n    ++int for_each_userdiff_driver(each_userdiff_driver_fn fn, void *cb_data)\n    ++{\n    ++\tint ret;\n    ++\n    ++\tret = for_each_userdiff_driver_list(fn, USERDIFF_DRIVER_TYPE_CUSTOM,\n    ++\t\t\t\t\t    cb_data, drivers, ndrivers);\n    ++\tif (ret)\n    ++\t\treturn ret;\n    ++\n    ++\tret = for_each_userdiff_driver_list(fn, USERDIFF_DRIVER_TYPE_BUILTIN,\n    ++\t\t\t\t\t    cb_data, builtin_drivers,\n    ++\t\t\t\t\t    ARRAY_SIZE(builtin_drivers));\n    ++\tif (ret)\n    ++\t\treturn ret;\n     +\n    -+\t\tfor (i = 0; i < ARRAY_SIZE(builtin_drivers); i++) {\n    -+\t\t\tstruct userdiff_driver *drv = builtin_drivers + i;\n    -+\t\t\tret = fn(drv, USERDIFF_DRIVER_TYPE_BUILTIN, cb_data);\n    -+\t\t\tif (ret)\n    -+\t\t\t\treturn ret;\n    -+\t\t}\n    -+\t}\n     +\treturn 0;\n     +}\n     \n    @@ userdiff.h: struct userdiff_driver {\n      \tint textconv_want_cache;\n      };\n     +enum userdiff_driver_type {\n    -+\tUSERDIFF_DRIVER_TYPE_UNSPECIFIED = 1<<0,\n    -+\tUSERDIFF_DRIVER_TYPE_BUILTIN = 1<<1,\n    -+\tUSERDIFF_DRIVER_TYPE_CUSTOM = 1<<2,\n    ++\tUSERDIFF_DRIVER_TYPE_BUILTIN = 1<<0,\n    ++\tUSERDIFF_DRIVER_TYPE_CUSTOM = 1<<1,\n     +};\n     +typedef int (*each_userdiff_driver_fn)(struct userdiff_driver *,\n     +\t\t\t\t       enum userdiff_driver_type, void *);\n    @@ userdiff.h: struct userdiff_driver *userdiff_find_by_path(struct index_state *is\n      \t\t\t\t\t      struct userdiff_driver *driver);\n      \n     +/*\n    -+ * Iterate over each driver of type userdiff_driver_type, or\n    -+ * USERDIFF_DRIVER_TYPE_UNSPECIFIED for all of them. Return non-zero\n    -+ * to exit from the loop.\n    ++ * Iterate over all userdiff drivers. The userdiff_driver_type\n    ++ * argument to each_userdiff_driver_fn indicates their type. Return\n    ++ * non-zero to exit early from the loop.\n     + */\n    -+int for_each_userdiff_driver(each_userdiff_driver_fn,\n    -+\t\t\t     enum userdiff_driver_type, void *);\n    ++int for_each_userdiff_driver(each_userdiff_driver_fn, void *);\n     +\n      #endif /* USERDIFF */\n 6:  862f6ab5d6 =  5:  3eb7abd121 userdiff tests: explicitly test \"default\" pattern\n 7:  22a07591b7 !  6:  e90758a978 userdiff tests: list builtin drivers via test-tool\n    @@ t/helper/test-userdiff.c (new)\n     +#include \"test-tool.h\"\n     +#include \"cache.h\"\n     +#include \"userdiff.h\"\n    ++#include \"config.h\"\n     +\n     +static int driver_cb(struct userdiff_driver *driver,\n     +\t\t     enum userdiff_driver_type type, void *priv)\n     +{\n    -+\tif (driver->funcname.pattern)\n    ++\tenum userdiff_driver_type *want_type = priv;\n    ++\tif (type & *want_type && driver->funcname.pattern)\n     +\t\tputs(driver->name);\n     +\treturn 0;\n     +}\n     +\n    -+static int list_what(enum userdiff_driver_type type)\n    ++static int cmd__userdiff_config(const char *var, const char *value, void *cb)\n     +{\n    -+\treturn for_each_userdiff_driver(driver_cb, type, NULL);\n    ++\tif (userdiff_config(var, value) < 0)\n    ++\t\treturn -1;\n    ++\treturn 0;\n     +}\n     +\n     +int cmd__userdiff(int argc, const char **argv)\n     +{\n    ++\tenum userdiff_driver_type want = 0;\n     +\tif (argc != 2)\n     +\t\treturn 1;\n     +\n     +\tif (!strcmp(argv[1], \"list-drivers\"))\n    -+\t\treturn list_what(USERDIFF_DRIVER_TYPE_UNSPECIFIED);\n    ++\t\twant = (USERDIFF_DRIVER_TYPE_BUILTIN |\n    ++\t\t\tUSERDIFF_DRIVER_TYPE_CUSTOM);\n     +\telse if (!strcmp(argv[1], \"list-builtin-drivers\"))\n    -+\t\treturn list_what(USERDIFF_DRIVER_TYPE_BUILTIN);\n    ++\t\twant = USERDIFF_DRIVER_TYPE_BUILTIN;\n     +\telse if (!strcmp(argv[1], \"list-custom-drivers\"))\n    -+\t\treturn list_what(USERDIFF_DRIVER_TYPE_CUSTOM);\n    ++\t\twant = USERDIFF_DRIVER_TYPE_CUSTOM;\n     +\telse\n     +\t\treturn error(\"unknown argument %s\", argv[1]);\n    ++\n    ++\tif (want & USERDIFF_DRIVER_TYPE_CUSTOM) {\n    ++\t\tsetup_git_directory();\n    ++\t\tgit_config(cmd__userdiff_config, NULL);\n    ++\t}\n    ++\n    ++\tfor_each_userdiff_driver(driver_cb, &want);\n    ++\n    ++\treturn 0;\n     +}\n     \n      ## t/t4018-diff-funcname.sh ##\n    -@@ t/t4018-diff-funcname.sh: test_description='Test custom diff function name patterns'\n    - . ./test-lib.sh\n    +@@ t/t4018-diff-funcname.sh: test_expect_success 'setup' '\n    + \techo B >B.java\n    + '\n      \n    - test_expect_success 'setup' '\n    ++test_expect_success 'setup: test-tool userdiff' '\n     +\t# Make sure additions to builtin_drivers are sorted\n     +\ttest_when_finished \"rm builtin-drivers.sorted\" &&\n     +\ttest-tool userdiff list-builtin-drivers >builtin-drivers &&\n    @@ t/t4018-diff-funcname.sh: test_description='Test custom diff function name patte\n     +\tsort <builtin-drivers >builtin-drivers.sorted &&\n     +\ttest_cmp builtin-drivers.sorted builtin-drivers &&\n     +\n    - \t# a non-trivial custom pattern\n    - \tgit config diff.custom1.funcname \"!static\n    - !String\n    -@@ t/t4018-diff-funcname.sh: test_expect_success 'setup' '\n    - '\n    - \n    ++\t# Ditto, but \"custom\" requires the .git directory and config\n    ++\t# to be setup and read.\n    ++\ttest_when_finished \"rm custom-drivers.sorted\" &&\n    ++\ttest-tool userdiff list-custom-drivers >custom-drivers &&\n    ++\ttest_file_not_empty custom-drivers &&\n    ++\tsort <custom-drivers >custom-drivers.sorted &&\n    ++\ttest_cmp custom-drivers.sorted custom-drivers\n    ++'\n    ++\n      diffpatterns=\"\n     -\tada\n     -\tbash\n    @@ t/t4018-diff-funcname.sh: test_expect_success 'setup' '\n     -\trust\n     -\ttex\n     -\tdefault\n    +-\tcustom1\n    +-\tcustom2\n    +-\tcustom3\n     +\t$(cat builtin-drivers)\n    - \tcustom1\n    - \tcustom2\n    - \tcustom3\n    ++\t$(cat custom-drivers)\n    + \"\n    + \n    + for p in $diffpatterns\n 8:  7755db9501 =  7:  04bce275ab userdiff: remove support for \"broken\" tests\n 9:  4e0b4b42e1 =  8:  3583078715 blame tests: don't rely on t/t4018/ directory\n10:  ce98c61bf4 =  9:  548673260b blame tests: simplify userdiff driver test\n-- \n2.31.1.527.g9b8f7de2547\n\n"},{"id":"421228","messageId":"patch-03.10-dff2aa0d38-20210408T145833Z-avarab@gmail.com","threadId":"55144","inReplyTo":"cover-00.10-0000000000-20210408T145833Z-avarab@gmail.com","subject":"[PATCH v5 3/9] userdiff style: normalize pascal regex declaration","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-08T15:04:18Z","receivedAt":"2021-04-08T15:04:43Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Declare the pascal pattern consistently with how we declare the\nothers, not having \"\\n\" on one line by itself, but as part of the\npattern, and when there are alterations have the \"|\" at the start, not\nend of the line.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n userdiff.c | 5 ++---\n 1 file changed, 2 insertions(+), 3 deletions(-)\n\ndiff --git a/userdiff.c b/userdiff.c\nindex 33b0ce4020..978ae64155 100644\n--- a/userdiff.c\n+++ b/userdiff.c\n@@ -175,9 +175,8 @@ PATTERNS(\"objc\",\n \t \"|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?\"\n \t \"|[-+*/<>%&^|=!]=|--|\\\\+\\\\+|<<=?|>>=?|&&|\\\\|\\\\||::|->\"),\n PATTERNS(\"pascal\",\n-\t \"^(((class[ \\t]+)?(procedure|function)|constructor|destructor|interface|\"\n-\t\t\"implementation|initialization|finalization)[ \\t]*.*)$\"\n-\t \"\\n\"\n+\t \"^(((class[ \\t]+)?(procedure|function)|constructor|destructor|interface\"\n+\t \"|implementation|initialization|finalization)[ \\t]*.*)$\\n\"\n \t \"^(.*=[ \\t]*(class|record).*)$\",\n \t /* -- */\n \t \"[a-zA-Z_][a-zA-Z0-9_]*\"\n-- \n2.31.1.527.g9b8f7de2547\n\n"},{"id":"421229","messageId":"patch-04.10-1a3a61e389-20210408T145833Z-avarab@gmail.com","threadId":"55144","inReplyTo":"cover-00.10-0000000000-20210408T145833Z-avarab@gmail.com","subject":"[PATCH v5 4/9] userdiff: add and use for_each_userdiff_driver()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-08T15:04:19Z","receivedAt":"2021-04-08T15:04:44Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Refactor the userdiff_find_by_namelen() function so that a new\nfor_each_userdiff_driver() API function does most of the work.\n\nThis will be useful for the same reason we've got other for_each_*()\nAPI functions as part of various APIs, and will be used in a follow-up\ncommit.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n userdiff.c | 70 ++++++++++++++++++++++++++++++++++++++++++++----------\n userdiff.h | 13 ++++++++++\n 2 files changed, 71 insertions(+), 12 deletions(-)\n\ndiff --git a/userdiff.c b/userdiff.c\nindex 978ae64155..a667ccaa8c 100644\n--- a/userdiff.c\n+++ b/userdiff.c\n@@ -259,20 +259,33 @@ static struct userdiff_driver driver_false = {\n \t{ NULL, 0 }\n };\n \n-static struct userdiff_driver *userdiff_find_by_namelen(const char *k, size_t len)\n+struct find_by_namelen_data {\n+\tconst char *name;\n+\tsize_t len;\n+\tstruct userdiff_driver *driver;\n+};\n+\n+static int userdiff_find_by_namelen_cb(struct userdiff_driver *driver,\n+\t\t\t\t       enum userdiff_driver_type type, void *priv)\n {\n-\tint i;\n-\tfor (i = 0; i < ndrivers; i++) {\n-\t\tstruct userdiff_driver *drv = drivers + i;\n-\t\tif (!strncmp(drv->name, k, len) && !drv->name[len])\n-\t\t\treturn drv;\n-\t}\n-\tfor (i = 0; i < ARRAY_SIZE(builtin_drivers); i++) {\n-\t\tstruct userdiff_driver *drv = builtin_drivers + i;\n-\t\tif (!strncmp(drv->name, k, len) && !drv->name[len])\n-\t\t\treturn drv;\n+\tstruct find_by_namelen_data *cb_data = priv;\n+\n+\tif (!strncmp(driver->name, cb_data->name, cb_data->len) &&\n+\t    !driver->name[cb_data->len]) {\n+\t\tcb_data->driver = driver;\n+\t\treturn 1; /* tell the caller to stop iterating */\n \t}\n-\treturn NULL;\n+\treturn 0;\n+}\n+\n+static struct userdiff_driver *userdiff_find_by_namelen(const char *name, size_t len)\n+{\n+\tstruct find_by_namelen_data udcbdata = {\n+\t\t.name = name,\n+\t\t.len = len,\n+\t};\n+\tfor_each_userdiff_driver(userdiff_find_by_namelen_cb, &udcbdata);\n+\treturn udcbdata.driver;\n }\n \n static int parse_funcname(struct userdiff_funcname *f, const char *k,\n@@ -379,3 +392,36 @@ struct userdiff_driver *userdiff_get_textconv(struct repository *r,\n \n \treturn driver;\n }\n+\n+static int for_each_userdiff_driver_list(each_userdiff_driver_fn fn,\n+\t\t\t\t\t enum userdiff_driver_type type, void *cb_data,\n+\t\t\t\t\t struct userdiff_driver *drv,\n+\t\t\t\t\t int drv_size)\n+{\n+\tint i;\n+\tint ret;\n+\tfor (i = 0; i < drv_size; i++) {\n+\t\tstruct userdiff_driver *item = drv + i;\n+\t\tif ((ret = fn(item, type, cb_data)))\n+\t\t\treturn ret;\n+\t}\n+\treturn 0;\n+}\n+\n+int for_each_userdiff_driver(each_userdiff_driver_fn fn, void *cb_data)\n+{\n+\tint ret;\n+\n+\tret = for_each_userdiff_driver_list(fn, USERDIFF_DRIVER_TYPE_CUSTOM,\n+\t\t\t\t\t    cb_data, drivers, ndrivers);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = for_each_userdiff_driver_list(fn, USERDIFF_DRIVER_TYPE_BUILTIN,\n+\t\t\t\t\t    cb_data, builtin_drivers,\n+\t\t\t\t\t    ARRAY_SIZE(builtin_drivers));\n+\tif (ret)\n+\t\treturn ret;\n+\n+\treturn 0;\n+}\ndiff --git a/userdiff.h b/userdiff.h\nindex 203057e13e..aee91bc77e 100644\n--- a/userdiff.h\n+++ b/userdiff.h\n@@ -21,6 +21,12 @@ struct userdiff_driver {\n \tstruct notes_cache *textconv_cache;\n \tint textconv_want_cache;\n };\n+enum userdiff_driver_type {\n+\tUSERDIFF_DRIVER_TYPE_BUILTIN = 1<<0,\n+\tUSERDIFF_DRIVER_TYPE_CUSTOM = 1<<1,\n+};\n+typedef int (*each_userdiff_driver_fn)(struct userdiff_driver *,\n+\t\t\t\t       enum userdiff_driver_type, void *);\n \n int userdiff_config(const char *k, const char *v);\n struct userdiff_driver *userdiff_find_by_name(const char *name);\n@@ -34,4 +40,11 @@ struct userdiff_driver *userdiff_find_by_path(struct index_state *istate,\n struct userdiff_driver *userdiff_get_textconv(struct repository *r,\n \t\t\t\t\t      struct userdiff_driver *driver);\n \n+/*\n+ * Iterate over all userdiff drivers. The userdiff_driver_type\n+ * argument to each_userdiff_driver_fn indicates their type. Return\n+ * non-zero to exit early from the loop.\n+ */\n+int for_each_userdiff_driver(each_userdiff_driver_fn, void *);\n+\n #endif /* USERDIFF */\n-- \n2.31.1.527.g9b8f7de2547\n\n"},{"id":"421230","messageId":"patch-06.10-e90758a978-20210408T145833Z-avarab@gmail.com","threadId":"55144","inReplyTo":"cover-00.10-0000000000-20210408T145833Z-avarab@gmail.com","subject":"[PATCH v5 6/9] userdiff tests: list builtin drivers via test-tool","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-08T15:04:21Z","receivedAt":"2021-04-08T15:04:45Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the userdiff test to list the builtin drivers via the\ntest-tool, using the new for_each_userdiff_driver() API function.\n\nThis gets rid of the need to modify this part of the test every time a\nnew pattern is added, see 2ff6c34612 (userdiff: support Bash,\n2020-10-22) and 09dad9256a (userdiff: support Markdown, 2020-05-02)\nfor two recent examples.\n\nI only need the \"list-builtin-drivers \"argument here, but let's add\n\"list-custom-drivers\" and \"list-drivers\" too, just because it's easy.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Makefile                 |  1 +\n t/helper/test-tool.c     |  1 +\n t/helper/test-tool.h     |  1 +\n t/helper/test-userdiff.c | 46 ++++++++++++++++++++++++++++++++++++++++\n t/t4018-diff-funcname.sh | 46 +++++++++++++++++-----------------------\n 5 files changed, 68 insertions(+), 27 deletions(-)\n create mode 100644 t/helper/test-userdiff.c\n\ndiff --git a/Makefile b/Makefile\nindex a6a73c5741..d2ba9bb402 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -752,6 +752,7 @@ TEST_BUILTINS_OBJS += test-submodule-nested-repo-config.o\n TEST_BUILTINS_OBJS += test-subprocess.o\n TEST_BUILTINS_OBJS += test-trace2.o\n TEST_BUILTINS_OBJS += test-urlmatch-normalization.o\n+TEST_BUILTINS_OBJS += test-userdiff.o\n TEST_BUILTINS_OBJS += test-wildmatch.o\n TEST_BUILTINS_OBJS += test-windows-named-pipe.o\n TEST_BUILTINS_OBJS += test-write-cache.o\ndiff --git a/t/helper/test-tool.c b/t/helper/test-tool.c\nindex 287aa60023..bf13b1f8c1 100644\n--- a/t/helper/test-tool.c\n+++ b/t/helper/test-tool.c\n@@ -72,6 +72,7 @@ static struct test_cmd cmds[] = {\n \t{ \"submodule-nested-repo-config\", cmd__submodule_nested_repo_config },\n \t{ \"subprocess\", cmd__subprocess },\n \t{ \"trace2\", cmd__trace2 },\n+\t{ \"userdiff\", cmd__userdiff },\n \t{ \"urlmatch-normalization\", cmd__urlmatch_normalization },\n \t{ \"xml-encode\", cmd__xml_encode },\n \t{ \"wildmatch\", cmd__wildmatch },\ndiff --git a/t/helper/test-tool.h b/t/helper/test-tool.h\nindex 9ea4b31011..698f95bf46 100644\n--- a/t/helper/test-tool.h\n+++ b/t/helper/test-tool.h\n@@ -62,6 +62,7 @@ int cmd__submodule_config(int argc, const char **argv);\n int cmd__submodule_nested_repo_config(int argc, const char **argv);\n int cmd__subprocess(int argc, const char **argv);\n int cmd__trace2(int argc, const char **argv);\n+int cmd__userdiff(int argc, const char **argv);\n int cmd__urlmatch_normalization(int argc, const char **argv);\n int cmd__xml_encode(int argc, const char **argv);\n int cmd__wildmatch(int argc, const char **argv);\ndiff --git a/t/helper/test-userdiff.c b/t/helper/test-userdiff.c\nnew file mode 100644\nindex 0000000000..f013f8a31e\n--- /dev/null\n+++ b/t/helper/test-userdiff.c\n@@ -0,0 +1,46 @@\n+#include \"test-tool.h\"\n+#include \"cache.h\"\n+#include \"userdiff.h\"\n+#include \"config.h\"\n+\n+static int driver_cb(struct userdiff_driver *driver,\n+\t\t     enum userdiff_driver_type type, void *priv)\n+{\n+\tenum userdiff_driver_type *want_type = priv;\n+\tif (type & *want_type && driver->funcname.pattern)\n+\t\tputs(driver->name);\n+\treturn 0;\n+}\n+\n+static int cmd__userdiff_config(const char *var, const char *value, void *cb)\n+{\n+\tif (userdiff_config(var, value) < 0)\n+\t\treturn -1;\n+\treturn 0;\n+}\n+\n+int cmd__userdiff(int argc, const char **argv)\n+{\n+\tenum userdiff_driver_type want = 0;\n+\tif (argc != 2)\n+\t\treturn 1;\n+\n+\tif (!strcmp(argv[1], \"list-drivers\"))\n+\t\twant = (USERDIFF_DRIVER_TYPE_BUILTIN |\n+\t\t\tUSERDIFF_DRIVER_TYPE_CUSTOM);\n+\telse if (!strcmp(argv[1], \"list-builtin-drivers\"))\n+\t\twant = USERDIFF_DRIVER_TYPE_BUILTIN;\n+\telse if (!strcmp(argv[1], \"list-custom-drivers\"))\n+\t\twant = USERDIFF_DRIVER_TYPE_CUSTOM;\n+\telse\n+\t\treturn error(\"unknown argument %s\", argv[1]);\n+\n+\tif (want & USERDIFF_DRIVER_TYPE_CUSTOM) {\n+\t\tsetup_git_directory();\n+\t\tgit_config(cmd__userdiff_config, NULL);\n+\t}\n+\n+\tfor_each_userdiff_driver(driver_cb, &want);\n+\n+\treturn 0;\n+}\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex cefe329aea..409372f3a4 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -25,34 +25,26 @@ test_expect_success 'setup' '\n \techo B >B.java\n '\n \n+test_expect_success 'setup: test-tool userdiff' '\n+\t# Make sure additions to builtin_drivers are sorted\n+\ttest_when_finished \"rm builtin-drivers.sorted\" &&\n+\ttest-tool userdiff list-builtin-drivers >builtin-drivers &&\n+\ttest_file_not_empty builtin-drivers &&\n+\tsort <builtin-drivers >builtin-drivers.sorted &&\n+\ttest_cmp builtin-drivers.sorted builtin-drivers &&\n+\n+\t# Ditto, but \"custom\" requires the .git directory and config\n+\t# to be setup and read.\n+\ttest_when_finished \"rm custom-drivers.sorted\" &&\n+\ttest-tool userdiff list-custom-drivers >custom-drivers &&\n+\ttest_file_not_empty custom-drivers &&\n+\tsort <custom-drivers >custom-drivers.sorted &&\n+\ttest_cmp custom-drivers.sorted custom-drivers\n+'\n+\n diffpatterns=\"\n-\tada\n-\tbash\n-\tbibtex\n-\tcpp\n-\tcsharp\n-\tcss\n-\tdts\n-\telixir\n-\tfortran\n-\tfountain\n-\tgolang\n-\thtml\n-\tjava\n-\tmarkdown\n-\tmatlab\n-\tobjc\n-\tpascal\n-\tperl\n-\tphp\n-\tpython\n-\truby\n-\trust\n-\ttex\n-\tdefault\n-\tcustom1\n-\tcustom2\n-\tcustom3\n+\t$(cat builtin-drivers)\n+\t$(cat custom-drivers)\n \"\n \n for p in $diffpatterns\n-- \n2.31.1.527.g9b8f7de2547\n\n"},{"id":"421231","messageId":"patch-05.10-3eb7abd121-20210408T145833Z-avarab@gmail.com","threadId":"55144","inReplyTo":"cover-00.10-0000000000-20210408T145833Z-avarab@gmail.com","subject":"[PATCH v5 5/9] userdiff tests: explicitly test \"default\" pattern","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-08T15:04:20Z","receivedAt":"2021-04-08T15:04:46Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Since 122aa6f9c0 (diff: introduce diff.<driver>.binary, 2008-10-05)\nthe internals of the userdiff.c code have understood a \"default\" name,\nwhich is invoked as userdiff_find_by_name(\"default\") and present in\nthe \"builtin_drivers\" struct. Let's test for this special case.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018-diff-funcname.sh | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex 9675bc17db..cefe329aea 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -49,6 +49,7 @@ diffpatterns=\"\n \truby\n \trust\n \ttex\n+\tdefault\n \tcustom1\n \tcustom2\n \tcustom3\n-- \n2.31.1.527.g9b8f7de2547\n\n"},{"id":"421232","messageId":"patch-07.10-04bce275ab-20210408T145833Z-avarab@gmail.com","threadId":"55144","inReplyTo":"cover-00.10-0000000000-20210408T145833Z-avarab@gmail.com","subject":"[PATCH v5 7/9] userdiff: remove support for \"broken\" tests","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-08T15:04:22Z","receivedAt":"2021-04-08T15:04:49Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"There have been no \"broken\" tests since 75c3b6b2e8 (userdiff: improve\nFortran xfuncname regex, 2020-08-12). Let's remove the test support\nfor them.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4018-diff-funcname.sh | 8 +-------\n t/t4018/README           | 3 ---\n 2 files changed, 1 insertion(+), 10 deletions(-)\n\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex 409372f3a4..740696c8f7 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -94,13 +94,7 @@ test_expect_success 'setup hunk header tests' '\n # check each individual file\n for i in $(git ls-files)\n do\n-\tif grep broken \"$i\" >/dev/null 2>&1\n-\tthen\n-\t\tresult=failure\n-\telse\n-\t\tresult=success\n-\tfi\n-\ttest_expect_$result \"hunk header: $i\" \"\n+\ttest_expect_success \"hunk header: $i\" \"\n \t\tgit diff -U1 $i >actual &&\n \t\tgrep '@@ .* @@.*RIGHT' actual\n \t\"\ndiff --git a/t/t4018/README b/t/t4018/README\nindex 283e01cca1..2d25b2b4fc 100644\n--- a/t/t4018/README\n+++ b/t/t4018/README\n@@ -7,9 +7,6 @@ at least two lines from the line that must appear in the hunk header.\n The text that must appear in the hunk header must contain the word\n \"right\", but in all upper-case, like in the title above.\n \n-To mark a test case that highlights a malfunction, insert the word\n-BROKEN in all lower-case somewhere in the file.\n-\n This text is a bit twisted and out of order, but it is itself a\n test case for the default hunk header pattern. Know what you are doing\n if you change it.\n-- \n2.31.1.527.g9b8f7de2547\n\n"},{"id":"421233","messageId":"patch-09.10-548673260b-20210408T145833Z-avarab@gmail.com","threadId":"55144","inReplyTo":"cover-00.10-0000000000-20210408T145833Z-avarab@gmail.com","subject":"[PATCH v5 9/9] blame tests: simplify userdiff driver test","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-08T15:04:24Z","receivedAt":"2021-04-08T15:04:50Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Simplify the test added in 9466e3809d (blame: enable funcname blaming\nwith userdiff driver, 2020-11-01) to use the --author support recently\nadded in 999cfc4f45 (test-lib functions: add --author support to\ntest_commit, 2021-01-12).\n\nWe also did not need the full fortran-external-function content. Let's\ncut it down to just the important parts.\n\nI'm modifying it to demonstrate that the fortran-specific userdiff\nfunction is in effect by adding \"DO NOT MATCH ...\" and \"AS THE ...\"\nlines surrounding the \"RIGHT\" one.\n\nThis is to check that we're using the userdiff \"fortran\" driver, as\nopposed to the default driver which would match on those lines as part\nof the general heuristic of matching a line that doesn't begin with\nwhitespace.\n\nThe test had also been leaving behind a .gitattributes file for later\ntests to possibly trip over, let's clean it up with\n\"test_when_finished\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/annotate-tests.sh | 36 +++++++++++++++---------------------\n 1 file changed, 15 insertions(+), 21 deletions(-)\n\ndiff --git a/t/annotate-tests.sh b/t/annotate-tests.sh\nindex 04a2c58594..d3b299e75c 100644\n--- a/t/annotate-tests.sh\n+++ b/t/annotate-tests.sh\n@@ -479,32 +479,26 @@ test_expect_success 'blame -L ^:RE (absolute: end-of-file)' '\n \tcheck_count -f hello.c -L$n -L^:ma.. F 4 G 1 H 1\n '\n \n-test_expect_success 'setup -L :funcname with userdiff driver' '\n-\techo \"fortran-* diff=fortran\" >.gitattributes &&\n-\tfortran_file=fortran-external-function &&\n-\tcat >$fortran_file <<-\\EOF &&\n+test_expect_success 'blame -L :funcname with userdiff driver' '\n+\tcat >file.template <<-\\EOF &&\n+\tDO NOT MATCH THIS LINE\n \tfunction RIGHT(a, b) result(c)\n+\tAS THE DEFAULT DRIVER WOULD\n \n \tinteger, intent(in) :: ChangeMe\n-\tinteger, intent(in) :: b\n-\tinteger, intent(out) :: c\n-\n-\tc = a+b\n-\n-\tend function RIGHT\n \tEOF\n-\tgit add \"$fortran_file\" &&\n-\tGIT_AUTHOR_NAME=\"A\" GIT_AUTHOR_EMAIL=\"A@test.git\" \\\n-\tgit commit -m \"add fortran file\" &&\n-\tsed -e \"s/ChangeMe/IWasChanged/\" <\"$fortran_file\" >\"$fortran_file\".tmp &&\n-\tmv \"$fortran_file\".tmp \"$fortran_file\" &&\n-\tgit add \"$fortran_file\" &&\n-\tGIT_AUTHOR_NAME=\"B\" GIT_AUTHOR_EMAIL=\"B@test.git\" \\\n-\tgit commit -m \"change fortran file\"\n-'\n \n-test_expect_success 'blame -L :funcname with userdiff driver' '\n-\tcheck_count -f fortran-external-function -L:RIGHT A 7 B 1\n+\tfortran_file=file.f03 &&\n+\ttest_when_finished \"rm .gitattributes\" &&\n+\techo \"$fortran_file diff=fortran\" >.gitattributes &&\n+\n+\ttest_commit --author \"A <A@test.git>\" \\\n+\t\t\"add\" \"$fortran_file\" \\\n+\t\t\"$(cat file.template)\" &&\n+\ttest_commit --author \"B <B@test.git>\" \\\n+\t\t\"change\" \"$fortran_file\" \\\n+\t\t\"$(cat file.template | sed -e s/ChangeMe/IWasChanged/)\" &&\n+\tcheck_count -f \"$fortran_file\" -L:RIGHT A 3 B 1\n '\n \n test_expect_success 'setup incremental' '\n-- \n2.31.1.527.g9b8f7de2547\n\n"},{"id":"421234","messageId":"patch-08.10-3583078715-20210408T145833Z-avarab@gmail.com","threadId":"55144","inReplyTo":"cover-00.10-0000000000-20210408T145833Z-avarab@gmail.com","subject":"[PATCH v5 8/9] blame tests: don't rely on t/t4018/ directory","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-08T15:04:23Z","receivedAt":"2021-04-08T15:04:53Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Refactor a test added in 9466e3809d (blame: enable funcname blaming\nwith userdiff driver, 2020-11-01) so that the blame tests don't rely\non stealing the contents of \"t/t4018/fortran-external-function\".\n\nI have another patch series that'll possibly (or not) refactor that\nfile, but having this test inter-dependency makes things simple in any\ncase by making this test more readable.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/annotate-tests.sh | 16 +++++++++++++---\n 1 file changed, 13 insertions(+), 3 deletions(-)\n\ndiff --git a/t/annotate-tests.sh b/t/annotate-tests.sh\nindex 29ce89090d..04a2c58594 100644\n--- a/t/annotate-tests.sh\n+++ b/t/annotate-tests.sh\n@@ -482,12 +482,22 @@ test_expect_success 'blame -L ^:RE (absolute: end-of-file)' '\n test_expect_success 'setup -L :funcname with userdiff driver' '\n \techo \"fortran-* diff=fortran\" >.gitattributes &&\n \tfortran_file=fortran-external-function &&\n-\torig_file=\"$TEST_DIRECTORY/t4018/$fortran_file\" &&\n-\tcp \"$orig_file\" . &&\n+\tcat >$fortran_file <<-\\EOF &&\n+\tfunction RIGHT(a, b) result(c)\n+\n+\tinteger, intent(in) :: ChangeMe\n+\tinteger, intent(in) :: b\n+\tinteger, intent(out) :: c\n+\n+\tc = a+b\n+\n+\tend function RIGHT\n+\tEOF\n \tgit add \"$fortran_file\" &&\n \tGIT_AUTHOR_NAME=\"A\" GIT_AUTHOR_EMAIL=\"A@test.git\" \\\n \tgit commit -m \"add fortran file\" &&\n-\tsed -e \"s/ChangeMe/IWasChanged/\" <\"$orig_file\" >\"$fortran_file\" &&\n+\tsed -e \"s/ChangeMe/IWasChanged/\" <\"$fortran_file\" >\"$fortran_file\".tmp &&\n+\tmv \"$fortran_file\".tmp \"$fortran_file\" &&\n \tgit add \"$fortran_file\" &&\n \tGIT_AUTHOR_NAME=\"B\" GIT_AUTHOR_EMAIL=\"B@test.git\" \\\n \tgit commit -m \"change fortran file\"\n-- \n2.31.1.527.g9b8f7de2547\n\n"},{"id":"421257","messageId":"xmqq1rbktw08.fsf@gitster.g","threadId":"55144","inReplyTo":"patch-04.10-1a3a61e389-20210408T145833Z-avarab@gmail.com","subject":"Re: [PATCH v5 4/9] userdiff: add and use for_each_userdiff_driver()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-04-08T19:29:59Z","receivedAt":"2021-04-08T19:30:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> +static int userdiff_find_by_namelen_cb(struct userdiff_driver *driver,\n> +\t\t\t\t       enum userdiff_driver_type type, void *priv)\n>  {\n> -\tint i;\n> -\tfor (i = 0; i < ndrivers; i++) {\n> -\t\tstruct userdiff_driver *drv = drivers + i;\n> -\t\tif (!strncmp(drv->name, k, len) && !drv->name[len])\n> -\t\t\treturn drv;\n> -\t}\n> -\tfor (i = 0; i < ARRAY_SIZE(builtin_drivers); i++) {\n> -\t\tstruct userdiff_driver *drv = builtin_drivers + i;\n> -\t\tif (!strncmp(drv->name, k, len) && !drv->name[len])\n> -\t\t\treturn drv;\n> +\tstruct find_by_namelen_data *cb_data = priv;\n> +\n> +\tif (!strncmp(driver->name, cb_data->name, cb_data->len) &&\n> +\t    !driver->name[cb_data->len]) {\n> +\t\tcb_data->driver = driver;\n> +\t\treturn 1; /* tell the caller to stop iterating */\n>  \t}\n> -\treturn NULL;\n> +\treturn 0;\n> +}\n> ...\n> +enum userdiff_driver_type {\n> +\tUSERDIFF_DRIVER_TYPE_BUILTIN = 1<<0,\n> +\tUSERDIFF_DRIVER_TYPE_CUSTOM = 1<<1,\n> +};\n> +typedef int (*each_userdiff_driver_fn)(struct userdiff_driver *,\n> +\t\t\t\t       enum userdiff_driver_type, void *);\n\nMakes sense.\n"},{"id":"421470","messageId":"87czv3gs3q.fsf@evledraar.gmail.com","threadId":"55144","inReplyTo":"YF1UuNBrAWvHSCp1@coredump.intra.peff.net","subject":"Re: [PATCH v4 05/10] userdiff: add and use for_each_userdiff_driver()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-09T19:44:57Z","receivedAt":"2021-04-09T19:45:04Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Fri, Mar 26 2021, Jeff King wrote:\n\n> On Thu, Mar 25, 2021 at 02:26:21AM +0100, Ævar Arnfjörð Bjarmason wrote:\n>\n>> >>     We could add a ifuncname and xifuncname or whatever for it I guess,\n>> >>     but currently the ICASE behavior in the C code is magic.\n>> >\n>> > Good point. IMHO that is something we should consider fixing\n>> > independently. It was a mistake to add builtins that couldn't be\n>> > replicated via the config (though I notice it happened quite a while\n>> > ago, and nobody seems to have cared, so perhaps it isn't that\n>> > important).\n>> \n>> I'm conspiring to eventually optimistically replace these ERE patterns\n>> with PCRE if we build with that at some point.\n>> \n>> Then you could just prefix your pattern with (?i) here in this and other\n>> things that want icase...\n>\n> I really like PCRE myself, but is it portable/common enough for us to\n> start using it for baked-in funcname patterns? I sort of assumed there\n> were exotic platforms where libpcre wouldn't build (or at least it would\n> be inconvenient or uncommon to have it). And it would be nice to degrade\n> those gracefully (or at least better than \"I guess you don't get any\n> builtin funcnames. Tough luck\").\n\nI think it's as portable as git, the JIT backend isn't, but PCRE itself\nis portably C code, and e.g. everything that python, PHP and any other\nnumber of things that depend on PCRE has had PCRE ported to it.\n\nThat plan involves an \"git rm -r compat/regex\" and a compat/pcre\ninstead, I have some long-left-over patches for that.\n"},{"id":"421473","messageId":"YHC1CZoOBJFwQIx8@coredump.intra.peff.net","threadId":"55144","inReplyTo":"87czv3gs3q.fsf@evledraar.gmail.com","subject":"Re: [PATCH v4 05/10] userdiff: add and use for_each_userdiff_driver()","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-04-09T20:11:53Z","receivedAt":"2021-04-09T20:11:56Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Apr 09, 2021 at 09:44:57PM +0200, Ævar Arnfjörð Bjarmason wrote:\n\n> > I really like PCRE myself, but is it portable/common enough for us to\n> > start using it for baked-in funcname patterns? I sort of assumed there\n> > were exotic platforms where libpcre wouldn't build (or at least it would\n> > be inconvenient or uncommon to have it). And it would be nice to degrade\n> > those gracefully (or at least better than \"I guess you don't get any\n> > builtin funcnames. Tough luck\").\n> \n> I think it's as portable as git, the JIT backend isn't, but PCRE itself\n> is portably C code, and e.g. everything that python, PHP and any other\n> number of things that depend on PCRE has had PCRE ported to it.\n> \n> That plan involves an \"git rm -r compat/regex\" and a compat/pcre\n> instead, I have some long-left-over patches for that.\n\nOK. I was more worried about platforms where it was cumbersome to\ninstall pcre. If we are shipping it as a vendored library, then at least\nthe dependency management is on us, and not the user. I do worry a\nlittle about running into complications with building or debugging it.\nIt would be importing a lot of new code (that in theory we never need to\nlook at, but that is not always how it works...). And while it may build\nportably everywhere, that may involve extra work and not integrate with\nour build knobs (e.g., it looks like it uses autoconf)\n\n-Peff\n"},{"id":"421491","messageId":"xmqq5z0vm6e2.fsf@gitster.g","threadId":"55144","inReplyTo":"YHC1CZoOBJFwQIx8@coredump.intra.peff.net","subject":"Re: [PATCH v4 05/10] userdiff: add and use for_each_userdiff_driver()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-04-09T22:37:25Z","receivedAt":"2021-04-09T22:37:28Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n>> That plan involves an \"git rm -r compat/regex\" and a compat/pcre\n>> instead, I have some long-left-over patches for that.\n>\n> OK. I was more worried about platforms where it was cumbersome to\n> install pcre. If we are shipping it as a vendored library, then at least\n> the dependency management is on us, and not the user. I do worry a\n> little about running into complications with building or debugging it.\n\nPlease don't.  I prefer not shipping compat/pcre ourselves; having\nto worry about version skew and picking up upstream security fixes\nin time, etc. for an external library that is well maintained is not\nwhat we need.\n\n\n"},{"id":"421527","messageId":"871rbigw4t.fsf@evledraar.gmail.com","threadId":"55144","inReplyTo":"xmqq5z0vm6e2.fsf@gitster.g","subject":"Re: [PATCH v4 05/10] userdiff: add and use for_each_userdiff_driver()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-10T12:30:10Z","receivedAt":"2021-04-10T12:30:15Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Sat, Apr 10 2021, Junio C Hamano wrote:\n\n> Jeff King <peff@peff.net> writes:\n>\n>>> That plan involves an \"git rm -r compat/regex\" and a compat/pcre\n>>> instead, I have some long-left-over patches for that.\n>>\n>> OK. I was more worried about platforms where it was cumbersome to\n>> install pcre. If we are shipping it as a vendored library, then at least\n>> the dependency management is on us, and not the user. I do worry a\n>> little about running into complications with building or debugging it.\n>\n> Please don't.  I prefer not shipping compat/pcre ourselves; having\n> to worry about version skew and picking up upstream security fixes\n> in time, etc. for an external library that is well maintained is not\n> what we need.\n\nI'm not submitting patches for this now, I personally don't care much if\nPCRE became a required dep (as in you need the library) and we didn't\nship it.\n\nI just assumed if I'd propose that that we'd want \"make\" to work on more\nbare-bones systems, since we e.g. carry a copy of\nsha1collisiondetection/ partially to facilitate that.\n\nBut FWIW, and to the extent I've got time etc. my rough plan for this\nlarger PCRE conpsiary was:\n\n 1. Get the current pickaxe-prep-for-PCRE patches to land.\n\n 2. I've got patches on top of that which move over to PCRE, so as with\n    the existing grep.c code we could drop kwset entirely (declare that\n    if you need optimized fast search, you can install PCRE).\n\n 3. git rm kwset.[ch]\n\n 4. Migrate more things to some light wrapper \"do a regex match\" API\n    (just small modifications to grep.c) that'll optimistically be able\n    to do BRE/ERE/PCRE matches. If you've got PCRE then PCRE can handle\n    them all (it's got a RX dialecttranslation interface).\n\n 5. All our regcomp/regexec should be gone in favor if this simple\n    replacement API.\n\nWhich is where this \"PCRE everywhere\" would come in, at this point we'll\nbe carrying a compat/regex/ which we could just as well replace with a\ncompat/pcre/.\n\nThe reason it would matter to have a hard prereq on PCRE is because it\nwould simplify and speed up a lot of code in various places to be able\nto make an assumption that it's there. E.g. for things that do \"match a\nline\" like pickaxe we could do clever anchoring and match the whole diff\nat once, and also things like streaming the diff out as we consume it\ninto PCRE's stream matching API.\n\nWe also have a lot of things purely on the C level that would be\nsimpler, e.g. (just one thing I remember) grep.c has 40 lines of code to\nemulate \\b<word>\\b, but we could ... just use \\b.\n\nTo Jeff's comment upthread:\n\n> And while it may build portably everywhere, that may involve extra\n> work and not integrate with our build knobs (e.g., it looks like it\n> uses autoconf)\n\nI already did that work in an old WIP branch I have. I haven't run it\nagain so maybe this summary is inaccurate, but here's the commit:\nhttps://github.com/avar/git/commit/79256ee4a1\n\nSo basically it won't build with autoconf or whatever, just with our own\nbuild system after we'd wget the relevant PCREv2 source files into\ncompat/pcre/.\n\nThe only things PCRE really needed autoconf or its own build system for\nwas to define 20-ish macros which are either options we can switch, or\n(looking at this now) 4x probes like \"HAVE_STDINT_H\" which we could add\nto compat.mak.uname and friends.\n\nIf we wanted it to Just Work we could run that script and \"git commit\"\nit, or just leave it at a make warning saying \"you don't have PCRE,\neither install it using your pkg system, or run this handy shellscript\".\n\nBut yeah, if Junio's paranoid about the security aspect we could skip\nthat \"./get-pcre.sh && git commit\" step.\n\nI'm getting some deja-vu writing this next part, I'm pretty sure I've\nwritten some version of the same E-Mail before, in any case:\n\n A. This wouldn't be anything new. We have some old bitrotting copy of\n    GNU awk's copy of glibc's regex engine in compat/regex. It's trivial\n    to e.g. get it to exhaust all memory on your system.\n\n    Sure, PCRE could have bugs, but at least we *could* update it. As\n    you may remember I abandoned my last attempt to update compat/regex/\n\n B. Even if you assume some bug like that, I think realistically no user\n    anywhere would be vulnerable to a copy we'd ship in compat/pcre/,\n    any vendor would ignore it and build the prereq themselves. It's\n    purely a developer aid.\n\n    Of course it could have some CVE, but it wouldn't be mean a git\n    point release.\n\n C. Even if the regex engine has an CVE/RCE we have no feature anywhere\n    to execute arbitrary user regexes, anyone who could inject one can\n    already run arbitrary git commands.\n\n D. The *one* exception to C) is gitweb's not-on-by-default \"grep the\n    source tree\" feature.\n\n    So first its users would be better off than they are now now. You\n    can trivially DoS any box that has it exposed, secondly the union of\n    gitweb users + those that would turn that on + build their own git\n    for running a public-facing site is probably 0 people these days.\n\n E. Even if you assume some combination of B..D isn't true I genuinely\n    don't know why this concern of PCRE being a special\n    security/vulnerability concern comes from, it seems to be brought up\n    any time the topic is discussed.\n\n    The implicit context is that we already process regexes using a\n    plethora of engines, and are using anything from AIXs, SunOS's,\n    Window's, OSX's, GNU libc's etc. regex engines to do so.\n\n    I haven't seen anything to suggest that PCRE's security record is\n    notably worse than even one of those pieces of software, and right\n    now we're exposing any bugs in the union of all those engines.\n\n    It's quite the opposite, unlike those engines PCRE actually gives\n    you really good features to limit resource consumption and regex\n    features in the event that you don't trust your regex or your\n    input.\n\n    I'd actually trust for things like processing a \"git grep on the\n    web\" with appropriate resource constraints.\n\n    The two solutions in that space are basically to use DFA engine as\n    e.g. Google's re2 does. That means living with a severely restricted\n    set of regex features, or to have bells and whistles as PCRE's NFA\n    does, but one that has tweakable resource limits.\n"}]}