{"thread":{"id":"57508","subject":"[GSoC][PATCH 0/1] userdiff: add buildin diff driver for JavaScript language","startedAt":"2022-03-04T13:09:36Z","lastAt":"2022-04-05T02:46:47Z","messageCount":25,"participants":["xing zhi jiang","Johannes Sixt","xing-zhi jiang","Glen Choo","Junio C Hamano","Ævar Arnfjörð Bjarmason"],"isPatch":true,"patchVersion":1,"patchTotal":1},"messages":[{"id":"450387","messageId":"20220304130854.857746-1-a97410985new@gmail.com","threadId":"57508","inReplyTo":null,"subject":"[GSoC][PATCH 0/1] userdiff: add buildin diff driver for JavaScript language","fromName":"xing zhi jiang","fromEmail":"a97410985new@gmail.com","sentAt":"2022-03-04T13:08:53Z","receivedAt":"2022-03-04T13:09:36Z","isPatch":true,"sender":{"key":"a97410985new@gmail.com","avatar":"https://gravatar.com/avatar/a66532d6438c89cb1989916e4a054b2c0c069296379adef42f9fbfca6afa6b06?d=mp&s=160"},"body":"I have already searched the git public-inbox don't find any active patch about\nuserdiff build-in driver for JavaScript(there is an unfinished patch about \nthree years ago). So I pick this as my GSoC micro project.\n\nBelow are typical function patterns from JavaScript, and \nalso has an example that matches the corresponding pattern. \nThese examples come from many popular JavaScript projects. \nBecause I want to make sure the hunk header would work well \non real-world projects.\n\nCommon function's pattern for JavaScript\n1. normal function\n  `^[\\t ]*((export[\\t ]+)?((async|get|set)[\\t ]+)?function[\\t ]*([\\t ]*\\\\*[\\t ]*|[\\t ]*)?[$_[:alpha:]][$_[:alnum:]]*[\\t ]*\\\\(.*)`\n  example: \n  1. https://github.com/mozilla/pdf.js/blob/85ff7b117e04471c550914b7a13193ab7f7ecc6a/src/display/canvas.js#L648\n  2. https://github.com/mozilla/pdf.js/blob/ad4b2ce021277ff7cea8ec7e32775c65d74ee673/test/unit/evaluator_spec.js#L40\n2. JavaScript variable declaration with a lambda expression\n  `^^[\\t ]*((const|let|var)[\\t ]*[$_[:alpha:]][$_[:alnum:]]*[\\t ]*=[\\t ]*\"\n\t \"(\\\\(.*\\\\)|[$_[:alpha:]][$_[:alnum:]]*[\\t ])[\\t ]*=>[\\t ]*\\\\{?)`\n   example:\n   1. https://github.com/webpack/webpack/blob/2279c5a2105ea1498b83a4854919aefe1a28c553/lib/ChunkGraph.js#L91\n   2. https://github.com/webpack/webpack/blob/2279c5a2105ea1498b83a4854919aefe1a28c553/lib/ChunkGraph.js#L122\n    \n   I found sometimes would define function on this way. But this should only match the top level? Because \n   it may match inside the function, and the below code would match the wrong function[1].\n\n3. exports methods by assigning an anonymous function\n  `^[\\t ]*(exports\\\\.[$_[:alpha:]][$_[:alnum:]]*[\\t ]*=[\\t ]*(\\\\(.*\\\\)|[$_[:alpha:]][$_[:alnum:]]*)[\\t ]*=>.*)`\n   example:\n   1. https://github.com/webpack/webpack/blob/c181294865dca01b28e6e316636fef5f2aad4eb6/lib/dependencies/DynamicExports.js#L17\n   2. https://github.com/ajaxorg/ace/blob/d95725983b363a616c584237013dfd36eaadbba4/lib/ace/lib/dom.js#L37\n4. expression about assign function to LHS\n  `^(.*=[\\t ]*function[\\t ]*([$_[:alpha:]][$_[:alnum:]]*)?[\\t ]*\\\\(.*)`\n   example:\n   1. https://github.com/ajaxorg/ace/blob/94422a4a892495564c56089af85019a8f8f24673/lib/ace/anchor.js#L102\n   2. https://github.com/ajaxorg/ace/blob/d95725983b363a616c584237013dfd36eaadbba4/lib/ace/lib/dom.js#L37\n   3. https://github.com/ajaxorg/ace/blob/4257621787b4253d6d493611f4ec5a37829da323/lib/ace/search.js#L350\n   4. https://github.com/mozilla/pdf.js/blob/85ff7b117e04471c550914b7a13193ab7f7ecc6a/src/display/canvas.js#L299\n   \n   Maybe this should only match on the 0,1,2 indent level? Because JavaScript may match the function assignment in another function.\n5. normal function in object literal\n  `^[\\t ]*([$_[:alpha:]][$_[:alnum:]]*[\\t ]*:[\\t ]*function[\\t ].*)`\n    1. https://github.com/jquery/jquery/blob/de5398a6ad088dc006b46c6a870a2a053f4cd663/src/core.js#L201\n    2. https://github.com/mozilla/pdf.js/blob/85ff7b117e04471c550914b7a13193ab7f7ecc6a/src/display/canvas.js#L242\n6. function in class\n  `^[\\t ]*((static[\\t ]+)?((async|get|set)[\\t ]+)?[$_[:alpha:]][$_[:alnum:]]*[\\t ]*\\\\(.*)`\n    \n   This regex is tricky because the class's function is no function keyword in JavaScript. \n   If you write the regex to match them, it will match many non-function declaration things!!! \n   Like examples below:\n   1. the non-function matches\n     1. https://github.com/ajaxorg/ace/blob/94422a4a892495564c56089af85019a8f8f24673/lib/ace/anchor.js#L58\n     2. https://github.com/ajaxorg/ace/blob/d95725983b363a616c584237013dfd36eaadbba4/lib/ace/lib/dom.js#L132\n   2. the function in class\n     1. https://github.com/mozilla/pdf.js/blob/85ff7b117e04471c550914b7a13193ab7f7ecc6a/src/display/canvas.js#L1929\n     2. https://github.com/webpack/webpack/blob/ccecc17c01af96edddb931a76e7a3b21ef2969d8/lib/Chunk.js#L179\n     3. https://github.com/webpack/webpack/blob/612de998f186a9bb2fe8769a91678df689a0541e/lib/Module.js#L242\n     4. https://github.com/mozilla/pdf.js/blob/5cf116a958548f6596674bf8d5ca0fe64aa2df3c/web/view_history.js#L75\n     5. https://github.com/mozilla/pdf.js/blob/5cf116a958548f6596674bf8d5ca0fe64aa2df3c/web/view_history.js#L89\n   \n    My solution is to add some negation rules, and one rule is skipping the keywords that may have characters immediately \n    following them in the parenthesis, rule is `!^[ \\t]*(if|do|while|for|with|switch|catch|import|return)`.\n    Another negation rule is only before this 「function in class」 regex, that skips the line's indent level more than \n    one because most of the function in class has one indent level(the class is on top-level). The negation rule is \n    `!^(\\t{2,}|[ ]{5,})`.\n    \n    But this is not enough, because maybe has function call on one indent level. So need an negation rule for skipping \n    statement. The negation rule is `!^.*;[ \\t]*`. But the bad news is JavaScript's statement can end without a semicolon. \n    So it still has an opportunity to recognize function call as the function declaration if the code's statement does not \n    end with semicolons.\n\nWord's pattern for JavaScript\nIn this part, I reference the formal ECMA specification heavily[2].\nJavaScript has some special syntax, such as numbers can be separated with an underscore for readability[3]. \nAnd has BigInt literal, which is number end with a 「n」 character. So the number-related regex would be some \ndifferences with another language.\n\nIn the last, I had a fork git project on Github. And has the CI's result, the all test cases pass[4].\n\n[1] https://github.com/webpack/webpack/blob/2279c5a2105ea1498b83a4854919aefe1a28c553/lib/ChunkGraph.js#L279\n[2] https://262.ecma-international.org/12.0/#sec-ecmascript-language-lexical-grammar\n[3] https://v8.dev/features/numeric-separators\n[4] https://github.com/a97410985/git/actions/runs/1933091300\n\nxing zhi jiang (1):\n  Add a diff driver for JavaScript languages.\n\n .gitignore                                    |  1 +\n Documentation/gitattributes.txt               |  2 +\n ...avascript-assignment-of-anonymous-function |  4 ++\n .../javascript-assignment-of-arrow-function   |  4 ++\n .../javascript-assignment-of-named-function   |  4 ++\n t/t4018/javascript-async-function             |  4 ++\n t/t4018/javascript-export-async-function      |  4 ++\n t/t4018/javascript-export-function            |  4 ++\n t/t4018/javascript-exports-anomyous-function  |  4 ++\n .../javascript-exports-anomyous-function-2    |  4 ++\n t/t4018/javascript-exports-function           |  4 ++\n t/t4018/javascript-function                   |  4 ++\n t/t4018/javascript-function-2                 | 10 ++++\n t/t4018/javascript-function-belong-to-IIFE    |  6 +++\n t/t4018/javascript-function-in-class          |  6 +++\n t/t4018/javascript-function-in-class-2        | 11 ++++\n t/t4018/javascript-function-in-object-literal |  7 +++\n t/t4018/javascript-generator-function         |  4 ++\n t/t4018/javascript-generator-function-2       |  4 ++\n t/t4018/javascript-getter-function-in-class   |  6 +++\n t/t4018/javascript-setter-function-in-class   |  6 +++\n .../javascript-skip-function-call-statement   |  7 +++\n t/t4018/javascript-skip-keywords              | 34 ++++++++++++\n t/t4018/javascript-static-function-in-class   |  6 +++\n t/t4034-diff-words.sh                         |  1 +\n t/t4034/javascript/expect                     | 52 +++++++++++++++++++\n t/t4034/javascript/post                       | 32 ++++++++++++\n t/t4034/javascript/pre                        | 32 ++++++++++++\n userdiff.c                                    | 38 ++++++++++++++\n 29 files changed, 305 insertions(+)\n create mode 100644 t/t4018/javascript-assignment-of-anonymous-function\n create mode 100644 t/t4018/javascript-assignment-of-arrow-function\n create mode 100644 t/t4018/javascript-assignment-of-named-function\n create mode 100644 t/t4018/javascript-async-function\n create mode 100644 t/t4018/javascript-export-async-function\n create mode 100644 t/t4018/javascript-export-function\n create mode 100644 t/t4018/javascript-exports-anomyous-function\n create mode 100644 t/t4018/javascript-exports-anomyous-function-2\n create mode 100644 t/t4018/javascript-exports-function\n create mode 100644 t/t4018/javascript-function\n create mode 100644 t/t4018/javascript-function-2\n create mode 100644 t/t4018/javascript-function-belong-to-IIFE\n create mode 100644 t/t4018/javascript-function-in-class\n create mode 100644 t/t4018/javascript-function-in-class-2\n create mode 100644 t/t4018/javascript-function-in-object-literal\n create mode 100644 t/t4018/javascript-generator-function\n create mode 100644 t/t4018/javascript-generator-function-2\n create mode 100644 t/t4018/javascript-getter-function-in-class\n create mode 100644 t/t4018/javascript-setter-function-in-class\n create mode 100644 t/t4018/javascript-skip-function-call-statement\n create mode 100644 t/t4018/javascript-skip-keywords\n create mode 100644 t/t4018/javascript-static-function-in-class\n create mode 100644 t/t4034/javascript/expect\n create mode 100644 t/t4034/javascript/post\n create mode 100644 t/t4034/javascript/pre\n\n\nbase-commit: e6ebfd0e8cbbd10878070c8a356b5ad1b3ca464e\n-- \n2.35.1.273.ge6ebfd0e8c.dirty\n\n"},{"id":"450388","messageId":"20220304130854.857746-2-a97410985new@gmail.com","threadId":"57508","inReplyTo":"20220304130854.857746-1-a97410985new@gmail.com","subject":"[GSoC][PATCH 1/1] Add a diff driver for JavaScript languages.","fromName":"xing zhi jiang","fromEmail":"a97410985new@gmail.com","sentAt":"2022-03-04T13:08:54Z","receivedAt":"2022-03-04T13:09:38Z","isPatch":true,"sender":{"key":"a97410985new@gmail.com","avatar":"https://gravatar.com/avatar/a66532d6438c89cb1989916e4a054b2c0c069296379adef42f9fbfca6afa6b06?d=mp&s=160"},"body":"In the xfunction part that matches normal functions,\na variable declaration with an assignment of function, the function declaration\nin the class, and also the function is object literal's property.\n\nAnd in the word regex part, that matches numbers, punctuations, and also the\nJavaScript identifier.\nThis part heavily references the formal ECMA sepcification[1].\n\n[1]https://262.ecma-international.org/12.0/#sec-ecmascript-language-lexical-grammar\n\nSigned-off-by: xing zhi jiang <a97410985new@gmail.com>\n---\n .gitignore                                    |  1 +\n Documentation/gitattributes.txt               |  2 +\n ...avascript-assignment-of-anonymous-function |  4 ++\n .../javascript-assignment-of-arrow-function   |  4 ++\n .../javascript-assignment-of-named-function   |  4 ++\n t/t4018/javascript-async-function             |  4 ++\n t/t4018/javascript-export-async-function      |  4 ++\n t/t4018/javascript-export-function            |  4 ++\n t/t4018/javascript-exports-anomyous-function  |  4 ++\n .../javascript-exports-anomyous-function-2    |  4 ++\n t/t4018/javascript-exports-function           |  4 ++\n t/t4018/javascript-function                   |  4 ++\n t/t4018/javascript-function-2                 | 10 ++++\n t/t4018/javascript-function-belong-to-IIFE    |  6 +++\n t/t4018/javascript-function-in-class          |  6 +++\n t/t4018/javascript-function-in-class-2        | 11 ++++\n t/t4018/javascript-function-in-object-literal |  7 +++\n t/t4018/javascript-generator-function         |  4 ++\n t/t4018/javascript-generator-function-2       |  4 ++\n t/t4018/javascript-getter-function-in-class   |  6 +++\n t/t4018/javascript-setter-function-in-class   |  6 +++\n .../javascript-skip-function-call-statement   |  7 +++\n t/t4018/javascript-skip-keywords              | 34 ++++++++++++\n t/t4018/javascript-static-function-in-class   |  6 +++\n t/t4034-diff-words.sh                         |  1 +\n t/t4034/javascript/expect                     | 52 +++++++++++++++++++\n t/t4034/javascript/post                       | 32 ++++++++++++\n t/t4034/javascript/pre                        | 32 ++++++++++++\n userdiff.c                                    | 38 ++++++++++++++\n 29 files changed, 305 insertions(+)\n create mode 100644 t/t4018/javascript-assignment-of-anonymous-function\n create mode 100644 t/t4018/javascript-assignment-of-arrow-function\n create mode 100644 t/t4018/javascript-assignment-of-named-function\n create mode 100644 t/t4018/javascript-async-function\n create mode 100644 t/t4018/javascript-export-async-function\n create mode 100644 t/t4018/javascript-export-function\n create mode 100644 t/t4018/javascript-exports-anomyous-function\n create mode 100644 t/t4018/javascript-exports-anomyous-function-2\n create mode 100644 t/t4018/javascript-exports-function\n create mode 100644 t/t4018/javascript-function\n create mode 100644 t/t4018/javascript-function-2\n create mode 100644 t/t4018/javascript-function-belong-to-IIFE\n create mode 100644 t/t4018/javascript-function-in-class\n create mode 100644 t/t4018/javascript-function-in-class-2\n create mode 100644 t/t4018/javascript-function-in-object-literal\n create mode 100644 t/t4018/javascript-generator-function\n create mode 100644 t/t4018/javascript-generator-function-2\n create mode 100644 t/t4018/javascript-getter-function-in-class\n create mode 100644 t/t4018/javascript-setter-function-in-class\n create mode 100644 t/t4018/javascript-skip-function-call-statement\n create mode 100644 t/t4018/javascript-skip-keywords\n create mode 100644 t/t4018/javascript-static-function-in-class\n create mode 100644 t/t4034/javascript/expect\n create mode 100644 t/t4034/javascript/post\n create mode 100644 t/t4034/javascript/pre\n\ndiff --git a/.gitignore b/.gitignore\nindex f817c509ec..de628c53f8 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -244,3 +244,4 @@ Release/\n /git.VC.db\n *.dSYM\n /contrib/buildsystems/out\n+/.cache\n\\ No newline at end of file\ndiff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\nindex 60984a4682..f6554a4651 100644\n--- a/Documentation/gitattributes.txt\n+++ b/Documentation/gitattributes.txt\n@@ -828,6 +828,8 @@ patterns are available:\n \n - `java` suitable for source code in the Java language.\n \n+- `javascript suitable for source code in the JavaScript language.\n+\n - `markdown` suitable for Markdown documents.\n \n - `matlab` suitable for source code in the MATLAB and Octave languages.\ndiff --git a/t/t4018/javascript-assignment-of-anonymous-function b/t/t4018/javascript-assignment-of-anonymous-function\nnew file mode 100644\nindex 0000000000..5d4701e84c\n--- /dev/null\n+++ b/t/t4018/javascript-assignment-of-anonymous-function\n@@ -0,0 +1,4 @@\n+const RIGHT = function (a, b) {\n+\t\n+    return a + b; // ChangeMe\n+};\n\\ No newline at end of file\ndiff --git a/t/t4018/javascript-assignment-of-arrow-function b/t/t4018/javascript-assignment-of-arrow-function\nnew file mode 100644\nindex 0000000000..6ab73ccb7a\n--- /dev/null\n+++ b/t/t4018/javascript-assignment-of-arrow-function\n@@ -0,0 +1,4 @@\n+const RIGHT = (a, b) => {\n+\t\n+    return a + b; // ChangeMe\n+};\n\\ No newline at end of file\ndiff --git a/t/t4018/javascript-assignment-of-named-function b/t/t4018/javascript-assignment-of-named-function\nnew file mode 100644\nindex 0000000000..85d43123a6\n--- /dev/null\n+++ b/t/t4018/javascript-assignment-of-named-function\n@@ -0,0 +1,4 @@\n+const RIGHT = function test (a, b) {\n+\t\n+    return a + b; // ChangeMe\n+};\n\\ No newline at end of file\ndiff --git a/t/t4018/javascript-async-function b/t/t4018/javascript-async-function\nnew file mode 100644\nindex 0000000000..ebc7c8c05b\n--- /dev/null\n+++ b/t/t4018/javascript-async-function\n@@ -0,0 +1,4 @@\n+async function RIGHT(a, b) {\n+  \n+  return a + b; // ChangeMe\n+}\n\\ No newline at end of file\ndiff --git a/t/t4018/javascript-export-async-function b/t/t4018/javascript-export-async-function\nnew file mode 100644\nindex 0000000000..3cd60b7980\n--- /dev/null\n+++ b/t/t4018/javascript-export-async-function\n@@ -0,0 +1,4 @@\n+export async function RIGHT(a, b) {\n+  \n+  return a + b; // ChangeMe\n+}\n\\ No newline at end of file\ndiff --git a/t/t4018/javascript-export-function b/t/t4018/javascript-export-function\nnew file mode 100644\nindex 0000000000..177ddec779\n--- /dev/null\n+++ b/t/t4018/javascript-export-function\n@@ -0,0 +1,4 @@\n+export function RIGHT(a, b) {\n+  \n+  return a + b; // ChangeMe\n+}\n\\ No newline at end of file\ndiff --git a/t/t4018/javascript-exports-anomyous-function b/t/t4018/javascript-exports-anomyous-function\nnew file mode 100644\nindex 0000000000..45b0ecd659\n--- /dev/null\n+++ b/t/t4018/javascript-exports-anomyous-function\n@@ -0,0 +1,4 @@\n+exports.setFlagged = RIGHT => {\n+\t\n+    return ChangeMe;\n+};\n\\ No newline at end of file\ndiff --git a/t/t4018/javascript-exports-anomyous-function-2 b/t/t4018/javascript-exports-anomyous-function-2\nnew file mode 100644\nindex 0000000000..0c572bfde3\n--- /dev/null\n+++ b/t/t4018/javascript-exports-anomyous-function-2\n@@ -0,0 +1,4 @@\n+exports.RIGHT = (a, b, runtime) => {\n+\t\n+    return ChangeMe;\n+};\n\\ No newline at end of file\ndiff --git a/t/t4018/javascript-exports-function b/t/t4018/javascript-exports-function\nnew file mode 100644\nindex 0000000000..f1587fddac\n--- /dev/null\n+++ b/t/t4018/javascript-exports-function\n@@ -0,0 +1,4 @@\n+exports.RIGHT = function(document) {\n+    \n+    return ChangeMe\n+}\n\\ No newline at end of file\ndiff --git a/t/t4018/javascript-function b/t/t4018/javascript-function\nnew file mode 100644\nindex 0000000000..dd8ab54c9b\n--- /dev/null\n+++ b/t/t4018/javascript-function\n@@ -0,0 +1,4 @@\n+function RIGHT(a, b) {\n+\n+  return a + b; // ChangeMe\n+}\n\\ No newline at end of file\ndiff --git a/t/t4018/javascript-function-2 b/t/t4018/javascript-function-2\nnew file mode 100644\nindex 0000000000..d72063cdf0\n--- /dev/null\n+++ b/t/t4018/javascript-function-2\n@@ -0,0 +1,10 @@\n+function test(a, b) {\n+  return {\n+\t\t\tRIGHT: function () {\n+\t\t\t\tcurrentUpdateRemovedChunks.forEach(function (chunkId) {\n+\t\t\t\t\tdelete $installedChunks$[chunkId];\n+\t\t\t\t});\n+\t\t\t\tcurrentUpdateRemovedChunks = ChangeMe;\n+   }\n+  }\n+}\n\\ No newline at end of file\ndiff --git a/t/t4018/javascript-function-belong-to-IIFE b/t/t4018/javascript-function-belong-to-IIFE\nnew file mode 100644\nindex 0000000000..6a80118e8a\n--- /dev/null\n+++ b/t/t4018/javascript-function-belong-to-IIFE\n@@ -0,0 +1,6 @@\n+(function () {\n+  this.$RIGHT = function (needle, modifier) {\n+      let a = 5;\n+      return ChangeMe;\n+  };\n+}).call(aaaa.prototype);\n\\ No newline at end of file\ndiff --git a/t/t4018/javascript-function-in-class b/t/t4018/javascript-function-in-class\nnew file mode 100644\nindex 0000000000..4b2f9c37e0\n--- /dev/null\n+++ b/t/t4018/javascript-function-in-class\n@@ -0,0 +1,6 @@\n+class Test {\n+  RIGHT() {\n+    let a = 4;\n+    let b = ChangeMe;\n+  }\n+}\n\\ No newline at end of file\ndiff --git a/t/t4018/javascript-function-in-class-2 b/t/t4018/javascript-function-in-class-2\nnew file mode 100644\nindex 0000000000..402c4aecc3\n--- /dev/null\n+++ b/t/t4018/javascript-function-in-class-2\n@@ -0,0 +1,11 @@\n+class Test {\n+  RIGHT(\n+      aaaaaaaaaa,\n+      bbbbbbbbbb,\n+      cccccccccc,\n+      dddddddddd\n+  ) {\n+    let a = 4;\n+    let b = ChangeMe;\n+  }\n+}\n\\ No newline at end of file\ndiff --git a/t/t4018/javascript-function-in-object-literal b/t/t4018/javascript-function-in-object-literal\nnew file mode 100644\nindex 0000000000..37f7ef72ee\n--- /dev/null\n+++ b/t/t4018/javascript-function-in-object-literal\n@@ -0,0 +1,7 @@\n+const obj = {\n+    RIGHT: function (elems, callback, arg) {\n+        var length, value;\n+        // ...\n+        return ChangeMe\n+    }\n+}\n\\ No newline at end of file\ndiff --git a/t/t4018/javascript-generator-function b/t/t4018/javascript-generator-function\nnew file mode 100644\nindex 0000000000..caf0b9f04f\n--- /dev/null\n+++ b/t/t4018/javascript-generator-function\n@@ -0,0 +1,4 @@\n+function* RIGHT(a, b) {\n+  \n+  return a + b; // ChangeMe\n+}\n\\ No newline at end of file\ndiff --git a/t/t4018/javascript-generator-function-2 b/t/t4018/javascript-generator-function-2\nnew file mode 100644\nindex 0000000000..6fc3b74a0d\n--- /dev/null\n+++ b/t/t4018/javascript-generator-function-2\n@@ -0,0 +1,4 @@\n+function *RIGHT(a, b) {\n+  \n+  return a + b; // ChangeMe\n+}\n\\ No newline at end of file\ndiff --git a/t/t4018/javascript-getter-function-in-class b/t/t4018/javascript-getter-function-in-class\nnew file mode 100644\nindex 0000000000..0159541be7\n--- /dev/null\n+++ b/t/t4018/javascript-getter-function-in-class\n@@ -0,0 +1,6 @@\n+class Test {\n+  get RIGHT() {\n+    let a = 4;\n+    let b = ChangeMe;\n+  }\n+}\n\\ No newline at end of file\ndiff --git a/t/t4018/javascript-setter-function-in-class b/t/t4018/javascript-setter-function-in-class\nnew file mode 100644\nindex 0000000000..3e17f47aa2\n--- /dev/null\n+++ b/t/t4018/javascript-setter-function-in-class\n@@ -0,0 +1,6 @@\n+class Test {\n+  set RIGHT() {\n+    let a = 4;\n+    let b = ChangeMe;\n+  }\n+}\n\\ No newline at end of file\ndiff --git a/t/t4018/javascript-skip-function-call-statement b/t/t4018/javascript-skip-function-call-statement\nnew file mode 100644\nindex 0000000000..84b51514d2\n--- /dev/null\n+++ b/t/t4018/javascript-skip-function-call-statement\n@@ -0,0 +1,7 @@\n+class Test {\n+  static RIGHT() {\n+    haha();\n+    haha2()\n+    let b = ChangeMe;\n+  }\n+}\n\\ No newline at end of file\ndiff --git a/t/t4018/javascript-skip-keywords b/t/t4018/javascript-skip-keywords\nnew file mode 100644\nindex 0000000000..1ed56c08de\n--- /dev/null\n+++ b/t/t4018/javascript-skip-keywords\n@@ -0,0 +1,34 @@\n+function RIGHT(a, b) {\n+  import(\"./async1\")\n+  if (a > 1) {\n+    // ...\n+  }\n+  do {\n+    // ...\n+  } while (i < 5);\n+  for (const element of array1) {\n+    console.log(element)\n+  }\n+  with(o) {\n+    console.log(x)\n+  }\n+  switch (expr) {\n+    case 'a':\n+      // ...\n+      break;\n+    case 'b':\n+      // ...\n+      break;\n+    default:\n+      // ...\n+  }\n+  try {\n+    // ...\n+    return (a + c)\n+  } \n+  catch (error) {\n+    // ...\n+  }\n+\n+  return a + b; // ChangeMe\n+}\n\\ No newline at end of file\ndiff --git a/t/t4018/javascript-static-function-in-class b/t/t4018/javascript-static-function-in-class\nnew file mode 100644\nindex 0000000000..efbccaf113\n--- /dev/null\n+++ b/t/t4018/javascript-static-function-in-class\n@@ -0,0 +1,6 @@\n+class Test {\n+  static RIGHT() {\n+    let a = 4;\n+    let b = ChangeMe;\n+  }\n+}\n\\ No newline at end of file\ndiff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh\nindex d5abcf4b4c..33073edeca 100755\n--- a/t/t4034-diff-words.sh\n+++ b/t/t4034-diff-words.sh\n@@ -324,6 +324,7 @@ test_language_driver dts\n test_language_driver fortran\n test_language_driver html\n test_language_driver java\n+test_language_driver javascript\n test_language_driver matlab\n test_language_driver objc\n test_language_driver pascal\ndiff --git a/t/t4034/javascript/expect b/t/t4034/javascript/expect\nnew file mode 100644\nindex 0000000000..602513c651\n--- /dev/null\n+++ b/t/t4034/javascript/expect\n@@ -0,0 +1,52 @@\n+<BOLD>diff --git a/pre b/post<RESET>\n+<BOLD>index b72238f..8bc3e3a 100644<RESET>\n+<BOLD>--- a/pre<RESET>\n+<BOLD>+++ b/post<RESET>\n+<CYAN>@@ -1,32 +1,32 @@<RESET>\n+// DecimalLiteral<RESET>\n+<RED>123<RESET>\n+<RED>0.123<RESET>\n+<RED>0.123e+5<RESET>\n+<RED>0.123E+5<RESET>\n+<RED>0.123e5<RESET>\n+<RED>1222222222222222223334444n<RESET><GREEN>124<RESET>\n+<GREEN>0.124<RESET>\n+<GREEN>0.123e-5<RESET>\n+<GREEN>0.123E-5<RESET>\n+<GREEN>0.123E5<RESET>\n+<GREEN>12222222222222222233344445n<RESET>\n+// HexIntegerLiteral<RESET>\n+<RED>0x10<RESET>\n+<RED>0X6Fa1<RESET>\n+<RED>0x123_456<RESET>\n+<RED>0x1234182989812f1289an<RESET><GREEN>0x11<RESET>\n+<GREEN>0X5Fa1<RESET>\n+<GREEN>0x123_756<RESET>\n+<GREEN>0x1234182989812f1289bn<RESET>\n+// OctalIntegerLiteral<RESET>\n+<RED>05<RESET>\n+<RED>0o6<RESET>\n+<RED>0O7<RESET>\n+<RED>0512_567<RESET>\n+<RED>0o424242424242424242424242424242666666n<RESET><GREEN>06<RESET>\n+<GREEN>0o5<RESET>\n+<GREEN>0O4<RESET>\n+<GREEN>0511_567<RESET>\n+<GREEN>0o424242424242424242424242424242666667n<RESET>\n+// BinaryIntegerLiteral<RESET>\n+<RED>0b1001<RESET>\n+<RED>0B0110<RESET>\n+<RED>0b0001_1001_0011<RESET>\n+<RED>0b1111111111111111111111111111111111111n<RESET><GREEN>0b1101<RESET>\n+<GREEN>0B0010<RESET>\n+<GREEN>0b0001_1101_0011<RESET>\n+<GREEN>0b11111111111111000011111111111111111n<RESET>\n+// punctuations<RESET>\n+{<RED>a<RESET><GREEN>b<RESET>} (<RED>a<RESET><GREEN>b<RESET>)\n+<RED>a<RESET><GREEN>b<RESET>;\n+[<RED>1,<RESET>2<GREEN>,3<RESET>]\n+[<RED>1, 2,<RESET> ...<RED>params<RESET><GREEN>params_v2<RESET> ]\n+a<RED><=<RESET><GREEN>=<RESET>2 a<RED>>=<RESET><GREEN>=<RESET>2 a<RED>==<RESET><GREEN>=<RESET>2 a<RED>!=<RESET><GREEN>=<RESET>2 a<RED>===<RESET><GREEN>=<RESET>2 a<RED>!==<RESET><GREEN>=<RESET>2 a<RED>^=<RESET><GREEN>=<RESET>2 a<RED>=><RESET><GREEN>=<RESET>2\n+a<RED>+=<RESET><GREEN>-=<RESET>b a<RED>*=<RESET><GREEN>%=<RESET>b a<RED>**=<RESET><GREEN>&&=<RESET>b a<RED>||=<RESET><GREEN>|=<RESET>b\n+b<RED>+<RESET><GREEN>-<RESET>c a<RED>--<RESET><GREEN>++<RESET> a<RED>>><RESET><GREEN><<<RESET>b a<RED>>>><RESET><GREEN>>>>=<RESET>b a<RED>>>=<RESET><GREEN><<=<RESET>b\n+a<RED>&&<RESET><GREEN>&<RESET>b a<RED>||<RESET><GREEN>|<RESET>b a<RED>&&=<RESET><GREEN>??=<RESET>b\ndiff --git a/t/t4034/javascript/post b/t/t4034/javascript/post\nnew file mode 100644\nindex 0000000000..8bc3e3af12\n--- /dev/null\n+++ b/t/t4034/javascript/post\n@@ -0,0 +1,32 @@\n+// DecimalLiteral\n+124\n+0.124\n+0.123e-5\n+0.123E-5\n+0.123E5\n+12222222222222222233344445n\n+// HexIntegerLiteral\n+0x11\n+0X5Fa1\n+0x123_756\n+0x1234182989812f1289bn\n+// OctalIntegerLiteral\n+06\n+0o5\n+0O4\n+0511_567\n+0o424242424242424242424242424242666667n\n+// BinaryIntegerLiteral\n+0b1101\n+0B0010\n+0b0001_1101_0011\n+0b11111111111111000011111111111111111n\n+// punctuations\n+{b} (b)\n+b;\n+[2,3]\n+[ ...params_v2 ]\n+a=2 a=2 a=2 a=2 a=2 a=2 a=2 a=2\n+a-=b a%=b a&&=b a|=b\n+b-c a++ a<<b a>>>=b a<<=b\n+a&b a|b a??=b\n\\ No newline at end of file\ndiff --git a/t/t4034/javascript/pre b/t/t4034/javascript/pre\nnew file mode 100644\nindex 0000000000..b72238f779\n--- /dev/null\n+++ b/t/t4034/javascript/pre\n@@ -0,0 +1,32 @@\n+// DecimalLiteral\n+123\n+0.123\n+0.123e+5\n+0.123E+5\n+0.123e5\n+1222222222222222223334444n\n+// HexIntegerLiteral\n+0x10\n+0X6Fa1\n+0x123_456\n+0x1234182989812f1289an\n+// OctalIntegerLiteral\n+05\n+0o6\n+0O7\n+0512_567\n+0o424242424242424242424242424242666666n\n+// BinaryIntegerLiteral\n+0b1001\n+0B0110\n+0b0001_1001_0011\n+0b1111111111111111111111111111111111111n\n+// punctuations\n+{a} (a)\n+a;\n+[1,2]\n+[ 1, 2, ...params ]\n+a<=2 a>=2 a==2 a!=2 a===2 a!==2 a^=2 a=>2\n+a+=b a*=b a**=b a||=b\n+b+c a-- a>>b a>>>b a>>=b\n+a&&b a||b a&&=b\n\\ No newline at end of file\ndiff --git a/userdiff.c b/userdiff.c\nindex 8578cb0d12..a6a341e3c1 100644\n--- a/userdiff.c\n+++ b/userdiff.c\n@@ -168,6 +168,44 @@ PATTERNS(\"java\",\n \t \"|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?\"\n \t \"|[-+*/<>%&^|=!]=\"\n \t \"|--|\\\\+\\\\+|<<=?|>>>?=?|&&|\\\\|\\\\|\"),\n+\n+PATTERNS(\"javascript\",\n+\t /* don't match the expression may contain parenthesis, because it is not a function declaration */\n+\t \"!^[ \\t]*(if|do|while|for|with|switch|catch|import|return)\\n\"\n+\t /* don't match statement */\n+\t \"!^.*;[ \\t]*\\n\"\n+\t /* match normal function */\n+\t \"^[\\t ]*((export[\\t ]+)?(async[\\t ]+)?function[\\t ]*([\\t ]*\\\\*[\\t ]*|[\\t ]*)?[$_[:alpha:]][$_[:alnum:]]*[\\t ]*\\\\(.*)\\n\"\n+\t /* match JavaScript variable declaration with a lambda expression */\n+\t \"^[\\t ]*((const|let|var)[\\t ]*[$_[:alpha:]][$_[:alnum:]]*[\\t ]*=[\\t ]*\"\n+\t \"(\\\\(.*\\\\)|[$_[:alpha:]][$_[:alnum:]]*[\\t ])[\\t ]*=>[\\t ]*\\\\{?)\\n\"\n+\t /* match exports for anonymous fucntion */\n+\t \"^[\\t ]*(exports\\\\.[$_[:alpha:]][$_[:alnum:]]*[\\t ]*=[\\t ]*(\\\\(.*\\\\)|[$_[:alpha:]][$_[:alnum:]]*)[\\t ]*=>.*)\\n\"\n+\t /* match assign function to LHS */\n+\t \"^(.*=[\\t ]*function[\\t ]*([$_[:alpha:]][$_[:alnum:]]*)?[\\t ]*\\\\(.*)\\n\"\n+\t /* match normal function in object literal */\n+\t \"^[\\t ]*([$_[:alpha:]][$_[:alnum:]]*[\\t ]*:[\\t ]*function[\\t ].*)\\n\"\n+\t /* don't match the function in class, which has more than one ident level */\n+\t \"!^(\\t{2,}|[ ]{5,})\\n\"\n+\t /* match function in class */\n+\t \"^[\\t ]*((static[\\t ]+)?((async|get|set)[\\t ]+)?[$_[:alpha:]][$_[:alnum:]]*[\\t ]*\\\\(.*)\",\n+\t /* word regex */\n+\t /* hexIntegerLiteral and bigHexIntegerLiteral*/\n+\t \"0[xX][0-9a-fA-F][_0-9a-fA-F]*n?|\"\n+\t /* octalIntegerLiteral and bigOctalIntegerLiteral */\n+\t \"0[oO]?[0-7][_0-7]*n?|\"\n+\t /* binaryIntegerLiteral and bigBinaryIntegerLiteral */\n+\t \"(0[bB][01][_01]*n?)|\"\n+\t /* decimalLiteral */\n+\t \"(0|[1-9][_0-9]*)?\\\\.?[0-9][_0-9]*([eE][+-]?[_0-9]+)?|\"\n+\t /* bigDecimalLiteral */\n+\t \"(0|[1-9][_0-9]*)n|\"\n+\t /* punctuations */\n+\t \"\\\\{|\\\\}|\\\\(|\\\\)|\\\\.|\\\\.{3}|;|,|<|>|<=|>=|==|!=|={3}|!==|\\\\+|-|\\\\*|/|%|\\\\*{2}|\"\n+\t \"\\\\+{2}|--|<<|>>|>>>|&|\\\\||\\\\^|!|~|&&|\\\\|{2}|\\\\?{1,2}|:|=|\\\\+=|-=|\\\\*=|%=|\\\\*{2}=|\"\n+\t \"<<=|>>=|>>>=|&=|\\\\|=|\\\\^=|&&=|\\\\|{2}=|\\\\?{2}=|=>|\"\n+\t /* identifiers */\n+\t \"[$_[:alpha:]][$_[:alnum:]]*\"),\n PATTERNS(\"markdown\",\n \t \"^ {0,3}#{1,6}[ \\t].*\",\n \t /* -- */\n-- \n2.35.1.273.ge6ebfd0e8c.dirty\n\n"},{"id":"450489","messageId":"79bd770e-74f1-edf8-884b-c0f916732ce0@kdbg.org","threadId":"57508","inReplyTo":"20220304130854.857746-2-a97410985new@gmail.com","subject":"Re: [GSoC][PATCH 1/1] Add a diff driver for JavaScript languages.","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2022-03-05T10:16:46Z","receivedAt":"2022-03-05T10:16:59Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Thank you for your contribution!\n\nAm 04.03.22 um 14:08 schrieb xing zhi jiang:\n> In the xfunction part that matches normal functions,\n> a variable declaration with an assignment of function, the function declaration\n> in the class, and also the function is object literal's property.\n\nOn the first read, I stumbled over the last half-sentence. Is this\nJavaScript-lingo for the construct\n\n   x = { foo: function() { ... } }\n\nA parenthetical note in this regard would be helpful.\n\n> And in the word regex part, that matches numbers, punctuations, and also the\n> JavaScript identifier.\n> This part heavily references the formal ECMA sepcification[1].\n> \n> [1]https://262.ecma-international.org/12.0/#sec-ecmascript-language-lexical-grammar\n\nAfter having seen the regex, to follow the syntax specification is\nunnecessarily tight. If you follow my suggestions, the note should\nprobably be rewritten, but keeping the link to the language reference is\ncertainly helpful.\n\n> \n> Signed-off-by: xing zhi jiang <a97410985new@gmail.com>\n> ---\n>  .gitignore                                    |  1 +\n>  Documentation/gitattributes.txt               |  2 +\n>  ...avascript-assignment-of-anonymous-function |  4 ++\n>  .../javascript-assignment-of-arrow-function   |  4 ++\n>  .../javascript-assignment-of-named-function   |  4 ++\n>  t/t4018/javascript-async-function             |  4 ++\n>  t/t4018/javascript-export-async-function      |  4 ++\n>  t/t4018/javascript-export-function            |  4 ++\n>  t/t4018/javascript-exports-anomyous-function  |  4 ++\n>  .../javascript-exports-anomyous-function-2    |  4 ++\n>  t/t4018/javascript-exports-function           |  4 ++\n>  t/t4018/javascript-function                   |  4 ++\n>  t/t4018/javascript-function-2                 | 10 ++++\n>  t/t4018/javascript-function-belong-to-IIFE    |  6 +++\n>  t/t4018/javascript-function-in-class          |  6 +++\n>  t/t4018/javascript-function-in-class-2        | 11 ++++\n>  t/t4018/javascript-function-in-object-literal |  7 +++\n>  t/t4018/javascript-generator-function         |  4 ++\n>  t/t4018/javascript-generator-function-2       |  4 ++\n>  t/t4018/javascript-getter-function-in-class   |  6 +++\n>  t/t4018/javascript-setter-function-in-class   |  6 +++\n>  .../javascript-skip-function-call-statement   |  7 +++\n>  t/t4018/javascript-skip-keywords              | 34 ++++++++++++\n>  t/t4018/javascript-static-function-in-class   |  6 +++\n>  t/t4034-diff-words.sh                         |  1 +\n>  t/t4034/javascript/expect                     | 52 +++++++++++++++++++\n>  t/t4034/javascript/post                       | 32 ++++++++++++\n>  t/t4034/javascript/pre                        | 32 ++++++++++++\n>  userdiff.c                                    | 38 ++++++++++++++\n>  29 files changed, 305 insertions(+)\n>  create mode 100644 t/t4018/javascript-assignment-of-anonymous-function\n>  create mode 100644 t/t4018/javascript-assignment-of-arrow-function\n>  create mode 100644 t/t4018/javascript-assignment-of-named-function\n>  create mode 100644 t/t4018/javascript-async-function\n>  create mode 100644 t/t4018/javascript-export-async-function\n>  create mode 100644 t/t4018/javascript-export-function\n>  create mode 100644 t/t4018/javascript-exports-anomyous-function\n>  create mode 100644 t/t4018/javascript-exports-anomyous-function-2\n>  create mode 100644 t/t4018/javascript-exports-function\n>  create mode 100644 t/t4018/javascript-function\n>  create mode 100644 t/t4018/javascript-function-2\n>  create mode 100644 t/t4018/javascript-function-belong-to-IIFE\n>  create mode 100644 t/t4018/javascript-function-in-class\n>  create mode 100644 t/t4018/javascript-function-in-class-2\n>  create mode 100644 t/t4018/javascript-function-in-object-literal\n>  create mode 100644 t/t4018/javascript-generator-function\n>  create mode 100644 t/t4018/javascript-generator-function-2\n>  create mode 100644 t/t4018/javascript-getter-function-in-class\n>  create mode 100644 t/t4018/javascript-setter-function-in-class\n>  create mode 100644 t/t4018/javascript-skip-function-call-statement\n>  create mode 100644 t/t4018/javascript-skip-keywords\n>  create mode 100644 t/t4018/javascript-static-function-in-class\n>  create mode 100644 t/t4034/javascript/expect\n>  create mode 100644 t/t4034/javascript/post\n>  create mode 100644 t/t4034/javascript/pre\n> \n> diff --git a/.gitignore b/.gitignore\n> index f817c509ec..de628c53f8 100644\n> --- a/.gitignore\n> +++ b/.gitignore\n> @@ -244,3 +244,4 @@ Release/\n>  /git.VC.db\n>  *.dSYM\n>  /contrib/buildsystems/out\n> +/.cache\n> \\ No newline at end of file\n\nDo not include this change. It does not belong to this commit.\n\n> diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\n> index 60984a4682..f6554a4651 100644\n> --- a/Documentation/gitattributes.txt\n> +++ b/Documentation/gitattributes.txt\n> @@ -828,6 +828,8 @@ patterns are available:\n>  \n>  - `java` suitable for source code in the Java language.\n>  \n> +- `javascript suitable for source code in the JavaScript language.\n\nPlease do not forget the closing quote.\n\n> +\n>  - `markdown` suitable for Markdown documents.\n>  \n>  - `matlab` suitable for source code in the MATLAB and Octave languages.\n> diff --git a/t/t4018/javascript-assignment-of-anonymous-function b/t/t4018/javascript-assignment-of-anonymous-function\n> new file mode 100644\n> index 0000000000..5d4701e84c\n> --- /dev/null\n> +++ b/t/t4018/javascript-assignment-of-anonymous-function\n> @@ -0,0 +1,4 @@\n> +const RIGHT = function (a, b) {\n> +\t\n> +    return a + b; // ChangeMe\n> +};\n> \\ No newline at end of file\n\nNotice this \"No newline at end of file\". Please complete the last line\nof the file, i.e. do add the last line break. Same for all other new\nfiles introduced in this patch.\n\n> diff --git a/t/t4018/javascript-assignment-of-arrow-function b/t/t4018/javascript-assignment-of-arrow-function\n> new file mode 100644\n> index 0000000000..6ab73ccb7a\n> --- /dev/null\n> +++ b/t/t4018/javascript-assignment-of-arrow-function\n> @@ -0,0 +1,4 @@\n> +const RIGHT = (a, b) => {\n> +\t\n> +    return a + b; // ChangeMe\n> +};\n> \\ No newline at end of file\n> diff --git a/t/t4018/javascript-assignment-of-named-function b/t/t4018/javascript-assignment-of-named-function\n> new file mode 100644\n> index 0000000000..85d43123a6\n> --- /dev/null\n> +++ b/t/t4018/javascript-assignment-of-named-function\n> @@ -0,0 +1,4 @@\n> +const RIGHT = function test (a, b) {\n> +\t\n> +    return a + b; // ChangeMe\n> +};\n> \\ No newline at end of file\n> diff --git a/t/t4018/javascript-async-function b/t/t4018/javascript-async-function\n> new file mode 100644\n> index 0000000000..ebc7c8c05b\n> --- /dev/null\n> +++ b/t/t4018/javascript-async-function\n> @@ -0,0 +1,4 @@\n> +async function RIGHT(a, b) {\n> +  \n> +  return a + b; // ChangeMe\n> +}\n> \\ No newline at end of file\n> diff --git a/t/t4018/javascript-export-async-function b/t/t4018/javascript-export-async-function\n> new file mode 100644\n> index 0000000000..3cd60b7980\n> --- /dev/null\n> +++ b/t/t4018/javascript-export-async-function\n> @@ -0,0 +1,4 @@\n> +export async function RIGHT(a, b) {\n> +  \n> +  return a + b; // ChangeMe\n> +}\n> \\ No newline at end of file\n> diff --git a/t/t4018/javascript-export-function b/t/t4018/javascript-export-function\n> new file mode 100644\n> index 0000000000..177ddec779\n> --- /dev/null\n> +++ b/t/t4018/javascript-export-function\n> @@ -0,0 +1,4 @@\n> +export function RIGHT(a, b) {\n> +  \n> +  return a + b; // ChangeMe\n> +}\n> \\ No newline at end of file\n> diff --git a/t/t4018/javascript-exports-anomyous-function b/t/t4018/javascript-exports-anomyous-function\n> new file mode 100644\n> index 0000000000..45b0ecd659\n> --- /dev/null\n> +++ b/t/t4018/javascript-exports-anomyous-function\n> @@ -0,0 +1,4 @@\n> +exports.setFlagged = RIGHT => {\n> +\t\n> +    return ChangeMe;\n> +};\n> \\ No newline at end of file\n> diff --git a/t/t4018/javascript-exports-anomyous-function-2 b/t/t4018/javascript-exports-anomyous-function-2\n> new file mode 100644\n> index 0000000000..0c572bfde3\n> --- /dev/null\n> +++ b/t/t4018/javascript-exports-anomyous-function-2\n> @@ -0,0 +1,4 @@\n> +exports.RIGHT = (a, b, runtime) => {\n> +\t\n> +    return ChangeMe;\n> +};\n> \\ No newline at end of file\n> diff --git a/t/t4018/javascript-exports-function b/t/t4018/javascript-exports-function\n> new file mode 100644\n> index 0000000000..f1587fddac\n> --- /dev/null\n> +++ b/t/t4018/javascript-exports-function\n> @@ -0,0 +1,4 @@\n> +exports.RIGHT = function(document) {\n> +    \n> +    return ChangeMe\n> +}\n\nNot a particularly important point, but the correct syntax requires a\nsemicolon here, I guess.\n\n> \\ No newline at end of file\n> diff --git a/t/t4018/javascript-function b/t/t4018/javascript-function\n> new file mode 100644\n> index 0000000000..dd8ab54c9b\n> --- /dev/null\n> +++ b/t/t4018/javascript-function\n> @@ -0,0 +1,4 @@\n> +function RIGHT(a, b) {\n> +\n> +  return a + b; // ChangeMe\n> +}\n> \\ No newline at end of file\n> diff --git a/t/t4018/javascript-function-2 b/t/t4018/javascript-function-2\n> new file mode 100644\n> index 0000000000..d72063cdf0\n> --- /dev/null\n> +++ b/t/t4018/javascript-function-2\n> @@ -0,0 +1,10 @@\n> +function test(a, b) {\n> +  return {\n> +\t\t\tRIGHT: function () {\n> +\t\t\t\tcurrentUpdateRemovedChunks.forEach(function (chunkId) {\n\nThis line is a decoy that is not picked up. Nice.\n\n> +\t\t\t\t\tdelete $installedChunks$[chunkId];\n> +\t\t\t\t});\n> +\t\t\t\tcurrentUpdateRemovedChunks = ChangeMe;\n> +   }\n> +  }\n> +}\n> \\ No newline at end of file\n> diff --git a/t/t4018/javascript-function-belong-to-IIFE b/t/t4018/javascript-function-belong-to-IIFE\n> new file mode 100644\n> index 0000000000..6a80118e8a\n> --- /dev/null\n> +++ b/t/t4018/javascript-function-belong-to-IIFE\n> @@ -0,0 +1,6 @@\n> +(function () {\n> +  this.$RIGHT = function (needle, modifier) {\n> +      let a = 5;\n> +      return ChangeMe;\n> +  };\n> +}).call(aaaa.prototype);\n> \\ No newline at end of file\n> diff --git a/t/t4018/javascript-function-in-class b/t/t4018/javascript-function-in-class\n> new file mode 100644\n> index 0000000000..4b2f9c37e0\n> --- /dev/null\n> +++ b/t/t4018/javascript-function-in-class\n> @@ -0,0 +1,6 @@\n> +class Test {\n> +  RIGHT() {\n> +    let a = 4;\n> +    let b = ChangeMe;\n> +  }\n> +}\n> \\ No newline at end of file\n> diff --git a/t/t4018/javascript-function-in-class-2 b/t/t4018/javascript-function-in-class-2\n> new file mode 100644\n> index 0000000000..402c4aecc3\n> --- /dev/null\n> +++ b/t/t4018/javascript-function-in-class-2\n> @@ -0,0 +1,11 @@\n> +class Test {\n> +  RIGHT(\n> +      aaaaaaaaaa,\n> +      bbbbbbbbbb,\n> +      cccccccccc,\n> +      dddddddddd\n> +  ) {\n> +    let a = 4;\n> +    let b = ChangeMe;\n> +  }\n> +}\n> \\ No newline at end of file\n\nIn the above two, we see class member functions. Is there a test case\nwhere the function parameter is on the same line as the function name or\nis that one of the difficult cases?\n\n> diff --git a/t/t4018/javascript-function-in-object-literal b/t/t4018/javascript-function-in-object-literal\n> new file mode 100644\n> index 0000000000..37f7ef72ee\n> --- /dev/null\n> +++ b/t/t4018/javascript-function-in-object-literal\n> @@ -0,0 +1,7 @@\n> +const obj = {\n> +    RIGHT: function (elems, callback, arg) {\n> +        var length, value;\n> +        // ...\n> +        return ChangeMe\n> +    }\n> +}\n> \\ No newline at end of file\n> diff --git a/t/t4018/javascript-generator-function b/t/t4018/javascript-generator-function\n> new file mode 100644\n> index 0000000000..caf0b9f04f\n> --- /dev/null\n> +++ b/t/t4018/javascript-generator-function\n> @@ -0,0 +1,4 @@\n> +function* RIGHT(a, b) {\n> +  \n> +  return a + b; // ChangeMe\n> +}\n> \\ No newline at end of file\n> diff --git a/t/t4018/javascript-generator-function-2 b/t/t4018/javascript-generator-function-2\n> new file mode 100644\n> index 0000000000..6fc3b74a0d\n> --- /dev/null\n> +++ b/t/t4018/javascript-generator-function-2\n> @@ -0,0 +1,4 @@\n> +function *RIGHT(a, b) {\n> +  \n> +  return a + b; // ChangeMe\n> +}\n> \\ No newline at end of file\n> diff --git a/t/t4018/javascript-getter-function-in-class b/t/t4018/javascript-getter-function-in-class\n> new file mode 100644\n> index 0000000000..0159541be7\n> --- /dev/null\n> +++ b/t/t4018/javascript-getter-function-in-class\n> @@ -0,0 +1,6 @@\n> +class Test {\n> +  get RIGHT() {\n> +    let a = 4;\n> +    let b = ChangeMe;\n> +  }\n> +}\n> \\ No newline at end of file\n> diff --git a/t/t4018/javascript-setter-function-in-class b/t/t4018/javascript-setter-function-in-class\n> new file mode 100644\n> index 0000000000..3e17f47aa2\n> --- /dev/null\n> +++ b/t/t4018/javascript-setter-function-in-class\n> @@ -0,0 +1,6 @@\n> +class Test {\n> +  set RIGHT() {\n> +    let a = 4;\n> +    let b = ChangeMe;\n> +  }\n> +}\n> \\ No newline at end of file\n> diff --git a/t/t4018/javascript-skip-function-call-statement b/t/t4018/javascript-skip-function-call-statement\n> new file mode 100644\n> index 0000000000..84b51514d2\n> --- /dev/null\n> +++ b/t/t4018/javascript-skip-function-call-statement\n> @@ -0,0 +1,7 @@\n> +class Test {\n> +  static RIGHT() {\n> +    haha();\n> +    haha2()\n> +    let b = ChangeMe;\n> +  }\n> +}\n\nGood call to include this test case!\n\n> \\ No newline at end of file\n> diff --git a/t/t4018/javascript-skip-keywords b/t/t4018/javascript-skip-keywords\n> new file mode 100644\n> index 0000000000..1ed56c08de\n> --- /dev/null\n> +++ b/t/t4018/javascript-skip-keywords\n> @@ -0,0 +1,34 @@\n> +function RIGHT(a, b) {\n> +  import(\"./async1\")\n> +  if (a > 1) {\n> +    // ...\n> +  }\n> +  do {\n> +    // ...\n> +  } while (i < 5);\n> +  for (const element of array1) {\n> +    console.log(element)\n> +  }\n> +  with(o) {\n> +    console.log(x)\n> +  }\n> +  switch (expr) {\n> +    case 'a':\n> +      // ...\n> +      break;\n> +    case 'b':\n> +      // ...\n> +      break;\n> +    default:\n> +      // ...\n> +  }\n> +  try {\n> +    // ...\n> +    return (a + c)\n> +  } \n> +  catch (error) {\n> +    // ...\n> +  }\n> +\n> +  return a + b; // ChangeMe\n> +}\n\nVery well!\n\n> \\ No newline at end of file\n> diff --git a/t/t4018/javascript-static-function-in-class b/t/t4018/javascript-static-function-in-class\n> new file mode 100644\n> index 0000000000..efbccaf113\n> --- /dev/null\n> +++ b/t/t4018/javascript-static-function-in-class\n> @@ -0,0 +1,6 @@\n> +class Test {\n> +  static RIGHT() {\n> +    let a = 4;\n> +    let b = ChangeMe;\n> +  }\n> +}\n> \\ No newline at end of file\n> diff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh\n> index d5abcf4b4c..33073edeca 100755\n> --- a/t/t4034-diff-words.sh\n> +++ b/t/t4034-diff-words.sh\n> @@ -324,6 +324,7 @@ test_language_driver dts\n>  test_language_driver fortran\n>  test_language_driver html\n>  test_language_driver java\n> +test_language_driver javascript\n>  test_language_driver matlab\n>  test_language_driver objc\n>  test_language_driver pascal\n> diff --git a/t/t4034/javascript/expect b/t/t4034/javascript/expect\n> new file mode 100644\n> index 0000000000..602513c651\n> --- /dev/null\n> +++ b/t/t4034/javascript/expect\n> @@ -0,0 +1,52 @@\n> +<BOLD>diff --git a/pre b/post<RESET>\n> +<BOLD>index b72238f..8bc3e3a 100644<RESET>\n> +<BOLD>--- a/pre<RESET>\n> +<BOLD>+++ b/post<RESET>\n> +<CYAN>@@ -1,32 +1,32 @@<RESET>\n> +// DecimalLiteral<RESET>\n> +<RED>123<RESET>\n> +<RED>0.123<RESET>\n> +<RED>0.123e+5<RESET>\n> +<RED>0.123E+5<RESET>\n> +<RED>0.123e5<RESET>\n> +<RED>1222222222222222223334444n<RESET><GREEN>124<RESET>\n> +<GREEN>0.124<RESET>\n> +<GREEN>0.123e-5<RESET>\n> +<GREEN>0.123E-5<RESET>\n> +<GREEN>0.123E5<RESET>\n> +<GREEN>12222222222222222233344445n<RESET>\n> +// HexIntegerLiteral<RESET>\n> +<RED>0x10<RESET>\n> +<RED>0X6Fa1<RESET>\n> +<RED>0x123_456<RESET>\n> +<RED>0x1234182989812f1289an<RESET><GREEN>0x11<RESET>\n> +<GREEN>0X5Fa1<RESET>\n> +<GREEN>0x123_756<RESET>\n> +<GREEN>0x1234182989812f1289bn<RESET>\n> +// OctalIntegerLiteral<RESET>\n> +<RED>05<RESET>\n> +<RED>0o6<RESET>\n> +<RED>0O7<RESET>\n> +<RED>0512_567<RESET>\n> +<RED>0o424242424242424242424242424242666666n<RESET><GREEN>06<RESET>\n> +<GREEN>0o5<RESET>\n> +<GREEN>0O4<RESET>\n> +<GREEN>0511_567<RESET>\n> +<GREEN>0o424242424242424242424242424242666667n<RESET>\n> +// BinaryIntegerLiteral<RESET>\n> +<RED>0b1001<RESET>\n> +<RED>0B0110<RESET>\n> +<RED>0b0001_1001_0011<RESET>\n> +<RED>0b1111111111111111111111111111111111111n<RESET><GREEN>0b1101<RESET>\n> +<GREEN>0B0010<RESET>\n> +<GREEN>0b0001_1101_0011<RESET>\n> +<GREEN>0b11111111111111000011111111111111111n<RESET>\n> +// punctuations<RESET>\n> +{<RED>a<RESET><GREEN>b<RESET>} (<RED>a<RESET><GREEN>b<RESET>)\n> +<RED>a<RESET><GREEN>b<RESET>;\n> +[<RED>1,<RESET>2<GREEN>,3<RESET>]\n> +[<RED>1, 2,<RESET> ...<RED>params<RESET><GREEN>params_v2<RESET> ]\n> +a<RED><=<RESET><GREEN>=<RESET>2 a<RED>>=<RESET><GREEN>=<RESET>2 a<RED>==<RESET><GREEN>=<RESET>2 a<RED>!=<RESET><GREEN>=<RESET>2 a<RED>===<RESET><GREEN>=<RESET>2 a<RED>!==<RESET><GREEN>=<RESET>2 a<RED>^=<RESET><GREEN>=<RESET>2 a<RED>=><RESET><GREEN>=<RESET>2\n> +a<RED>+=<RESET><GREEN>-=<RESET>b a<RED>*=<RESET><GREEN>%=<RESET>b a<RED>**=<RESET><GREEN>&&=<RESET>b a<RED>||=<RESET><GREEN>|=<RESET>b\n> +b<RED>+<RESET><GREEN>-<RESET>c a<RED>--<RESET><GREEN>++<RESET> a<RED>>><RESET><GREEN><<<RESET>b a<RED>>>><RESET><GREEN>>>>=<RESET>b a<RED>>>=<RESET><GREEN><<=<RESET>b\n> +a<RED>&&<RESET><GREEN>&<RESET>b a<RED>||<RESET><GREEN>|<RESET>b a<RED>&&=<RESET><GREEN>??=<RESET>b\n\nThis looks good! I see many changes in operators being tested.\n\n> diff --git a/t/t4034/javascript/post b/t/t4034/javascript/post\n> new file mode 100644\n> index 0000000000..8bc3e3af12\n> --- /dev/null\n> +++ b/t/t4034/javascript/post\n> @@ -0,0 +1,32 @@\n> +// DecimalLiteral\n> +124\n> +0.124\n> +0.123e-5\n> +0.123E-5\n> +0.123E5\n> +12222222222222222233344445n\n> +// HexIntegerLiteral\n> +0x11\n> +0X5Fa1\n> +0x123_756\n> +0x1234182989812f1289bn\n> +// OctalIntegerLiteral\n> +06\n> +0o5\n> +0O4\n> +0511_567\n> +0o424242424242424242424242424242666667n\n> +// BinaryIntegerLiteral\n> +0b1101\n> +0B0010\n> +0b0001_1101_0011\n> +0b11111111111111000011111111111111111n\n> +// punctuations\n> +{b} (b)\n> +b;\n> +[2,3]\n> +[ ...params_v2 ]\n> +a=2 a=2 a=2 a=2 a=2 a=2 a=2 a=2\n> +a-=b a%=b a&&=b a|=b\n> +b-c a++ a<<b a>>>=b a<<=b\n> +a&b a|b a??=b\n> \\ No newline at end of file\n> diff --git a/t/t4034/javascript/pre b/t/t4034/javascript/pre\n> new file mode 100644\n> index 0000000000..b72238f779\n> --- /dev/null\n> +++ b/t/t4034/javascript/pre\n> @@ -0,0 +1,32 @@\n> +// DecimalLiteral\n> +123\n> +0.123\n> +0.123e+5\n> +0.123E+5\n> +0.123e5\n> +1222222222222222223334444n\n> +// HexIntegerLiteral\n> +0x10\n> +0X6Fa1\n> +0x123_456\n> +0x1234182989812f1289an\n> +// OctalIntegerLiteral\n> +05\n> +0o6\n> +0O7\n> +0512_567\n> +0o424242424242424242424242424242666666n\n> +// BinaryIntegerLiteral\n> +0b1001\n> +0B0110\n> +0b0001_1001_0011\n> +0b1111111111111111111111111111111111111n\n> +// punctuations\n> +{a} (a)\n> +a;\n> +[1,2]\n> +[ 1, 2, ...params ]\n> +a<=2 a>=2 a==2 a!=2 a===2 a!==2 a^=2 a=>2\n> +a+=b a*=b a**=b a||=b\n> +b+c a-- a>>b a>>>b a>>=b\n> +a&&b a||b a&&=b\n> \\ No newline at end of file\n> diff --git a/userdiff.c b/userdiff.c\n> index 8578cb0d12..a6a341e3c1 100644\n> --- a/userdiff.c\n> +++ b/userdiff.c\n> @@ -168,6 +168,44 @@ PATTERNS(\"java\",\n>  \t \"|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?\"\n>  \t \"|[-+*/<>%&^|=!]=\"\n>  \t \"|--|\\\\+\\\\+|<<=?|>>>?=?|&&|\\\\|\\\\|\"),\n> +\n> +PATTERNS(\"javascript\",\n> +\t /* don't match the expression may contain parenthesis, because it is not a function declaration */\n> +\t \"!^[ \\t]*(if|do|while|for|with|switch|catch|import|return)\\n\"\n\nThese will not match\n\n\t}while (expr)\n\nnote the absent blank before the keyword, but that is an acceptable\ntrade-off to keep things simple. Good.\n\n> +\t /* don't match statement */\n> +\t \"!^.*;[ \\t]*\\n\"\n\nThis regexp can be reduced to\n\n\t\"!;\\n\"\n\nno?\n\n> +\t /* match normal function */\n> +\t \"^[\\t ]*((export[\\t ]+)?(async[\\t ]+)?function[\\t ]*([\\t ]*\\\\*[\\t ]*|[\\t ]*)?[$_[:alpha:]][$_[:alnum:]]*[\\t ]*\\\\(.*)\\n\"\n\nGood. One note though: keyword \"function\" can optionally be followed by\nan asterisk '*'. You can probably simplify the middle part to\n\n\t...function[\\t *]*...identifier...\n\n> +\t /* match JavaScript variable declaration with a lambda expression */\n> +\t \"^[\\t ]*((const|let|var)[\\t ]*[$_[:alpha:]][$_[:alnum:]]*[\\t ]*=[\\t ]*\"\n> +\t \"(\\\\(.*\\\\)|[$_[:alpha:]][$_[:alnum:]]*[\\t ])[\\t ]*=>[\\t ]*\\\\{?)\\n\"\n\nLet's break this down:\n\n\t\"^[\\t ]*\"\n\t    \"(\"\n\t        \"(const|let|var)[\\t ]*\"\n\t        \"[$_[:alpha:]][$_[:alnum:]]*[\\t ]*\"\n\t        \"=[\\t ]*\"\n\t        \"(\"\n\t            \"\\\\(.*\\\\)\"\n\t            \"|\"\n\t            \"[$_[:alpha:]][$_[:alnum:]]*[\\t ]\"\n\t        \")[\\t ]*\"\n\t        \"=>[\\t ]*\"\n\t        \"\\\\{?\"\n\t    \")\\n\"\n\nCan you not have\n\n\tvar f = foo=>{\n\nbecause I see that whitespace is required between the identifier and \"=>\"?\n\n> +\t /* match exports for anonymous fucntion */\n> +\t \"^[\\t ]*(exports\\\\.[$_[:alpha:]][$_[:alnum:]]*[\\t ]*=[\\t ]*(\\\\(.*\\\\)|[$_[:alpha:]][$_[:alnum:]]*)[\\t ]*=>.*)\\n\"\n\nHere, whitespace is not required. Is the above an oversight?\n\nBTW, can keyword \"exports\" be used for something other than functions?\n\n> +\t /* match assign function to LHS */\n> +\t \"^(.*=[\\t ]*function[\\t ]*([$_[:alpha:]][$_[:alnum:]]*)?[\\t ]*\\\\(.*)\\n\"\n\nThis makes me think that whenever keyword \"function\" appears, then we\nsee the beginning of a function. This would allow a simple pattern\nupfront that picks out all functions defined with this keyword, and all\nother patterns need only be concerned with the exceptional cases.\n\n\t/* \"function\" is first non-space token */\n\t\"^[\\t ]*function[\\t ].*)\\n\"\n\t/* \"function\" is not first token */\n\t\"^.*[^$_[:alnum:]]function[\\t ].*\\n\"\n\n> +\t /* match normal function in object literal */\n> +\t \"^[\\t ]*([$_[:alpha:]][$_[:alnum:]]*[\\t ]*:[\\t ]*function[\\t ].*)\\n\"\n> +\t /* don't match the function in class, which has more than one ident level */\n> +\t \"!^(\\t{2,}|[ ]{5,})\\n\"\n\nFor some, hopefully universally agreed upon in the JavaScript community,\ndefinition of indentation level. Ok...\n\n> +\t /* match function in class */\n> +\t \"^[\\t ]*((static[\\t ]+)?((async|get|set)[\\t ]+)?[$_[:alpha:]][$_[:alnum:]]*[\\t ]*\\\\(.*)\",\n\nAren't \"get\" and \"set\" as universal identifiers of functions or can they\noccur on other contexts? Thinking of it, they can occur in comments\neverywhere, so they would pick up too many matches if treated like my\n\"function\" proposal above.\n\n> +\t /* word regex */\n> +\t /* hexIntegerLiteral and bigHexIntegerLiteral*/\n> +\t \"0[xX][0-9a-fA-F][_0-9a-fA-F]*n?|\"\n> +\t /* octalIntegerLiteral and bigOctalIntegerLiteral */\n> +\t \"0[oO]?[0-7][_0-7]*n?|\"\n> +\t /* binaryIntegerLiteral and bigBinaryIntegerLiteral */\n> +\t \"(0[bB][01][_01]*n?)|\"\n> +\t /* decimalLiteral */\n> +\t \"(0|[1-9][_0-9]*)?\\\\.?[0-9][_0-9]*([eE][+-]?[_0-9]+)?|\"\n> +\t /* bigDecimalLiteral */\n> +\t \"(0|[1-9][_0-9]*)n|\"\n\nYou do not have to make the word-regex so tight that it excludes\nincorrect literals because you can assume that incorrect literals will\nnot occur. In particular integers beginning with a 0 need not be treated\nspecially. You can fold the octal and decimal integers into a single\nexpression:\n\n\t\"[0-9][oO]?[_0-9.]*)([eE][+-]?[_0-9]+)?n?\"\n\nAre floatingpoint literals beginning with a decimal point like .5 not\npermitted?\n\nPlease follow the custom to place the alternation character \"|\" at the\nbeginning of the next line, not at the end of the previous.\n\n> +\t /* punctuations */\n> +\t \"\\\\{|\\\\}|\\\\(|\\\\)|\\\\.|\\\\.{3}|;|,|<|>|<=|>=|==|!=|={3}|!==|\\\\+|-|\\\\*|/|%|\\\\*{2}|\"\n> +\t \"\\\\+{2}|--|<<|>>|>>>|&|\\\\||\\\\^|!|~|&&|\\\\|{2}|\\\\?{1,2}|:|=|\\\\+=|-=|\\\\*=|%=|\\\\*{2}=|\"\n> +\t \"<<=|>>=|>>>=|&=|\\\\|=|\\\\^=|&&=|\\\\|{2}=|\\\\?{2}=|=>|\"\n\nYou could collaps many of the operators into single alternatives, but if\nyou prefer it this way, it is fine, too. One example:\n\n\t\">{1,3}=\"\n\nBut please remove the single-character operators from the list, because\nthere is an implicit single-character alternative that is not visible in\nthe code here.\n\n> +\t /* identifiers */\n> +\t \"[$_[:alpha:]][$_[:alnum:]]*\"),\n>  PATTERNS(\"markdown\",\n>  \t \"^ {0,3}#{1,6}[ \\t].*\",\n>  \t /* -- */\n\n-- Hannes\n"},{"id":"450490","messageId":"f8812e54-e8de-634a-a04f-0995afce6ef4@kdbg.org","threadId":"57508","inReplyTo":"20220304130854.857746-1-a97410985new@gmail.com","subject":"Re: [GSoC][PATCH 0/1] userdiff: add buildin diff driver for JavaScript language","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2022-03-05T13:41:39Z","receivedAt":"2022-03-05T13:41:44Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 04.03.22 um 14:08 schrieb xing zhi jiang:\n> I have already searched the git public-inbox don't find any active patch about\n> userdiff build-in driver for JavaScript(there is an unfinished patch about \n> three years ago). So I pick this as my GSoC micro project.\n> \n> Below are typical function patterns from JavaScript, and \n> also has an example that matches the corresponding pattern. \n> These examples come from many popular JavaScript projects. \n> Because I want to make sure the hunk header would work well \n> on real-world projects.\n\nThank you for the thorough research. I've already responded to the patch\nabout the technical solution. Here are some general thoughts.\n\n> \n> Common function's pattern for JavaScript\n> 1. normal function\n>   `^[\\t ]*((export[\\t ]+)?((async|get|set)[\\t ]+)?function[\\t ]*([\\t ]*\\\\*[\\t ]*|[\\t ]*)?[$_[:alpha:]][$_[:alnum:]]*[\\t ]*\\\\(.*)`\n>   example: \n>   1. https://github.com/mozilla/pdf.js/blob/85ff7b117e04471c550914b7a13193ab7f7ecc6a/src/display/canvas.js#L648\n>   2. https://github.com/mozilla/pdf.js/blob/ad4b2ce021277ff7cea8ec7e32775c65d74ee673/test/unit/evaluator_spec.js#L40\n> 2. JavaScript variable declaration with a lambda expression\n>   `^^[\\t ]*((const|let|var)[\\t ]*[$_[:alpha:]][$_[:alnum:]]*[\\t ]*=[\\t ]*\"\n> \t \"(\\\\(.*\\\\)|[$_[:alpha:]][$_[:alnum:]]*[\\t ])[\\t ]*=>[\\t ]*\\\\{?)`\n>    example:\n>    1. https://github.com/webpack/webpack/blob/2279c5a2105ea1498b83a4854919aefe1a28c553/lib/ChunkGraph.js#L91\n>    2. https://github.com/webpack/webpack/blob/2279c5a2105ea1498b83a4854919aefe1a28c553/lib/ChunkGraph.js#L122\n>     \n>    I found sometimes would define function on this way. But this should only match the top level? Because \n>    it may match inside the function, and the below code would match the wrong function[1].\n\nFunctions nested in other functions in general lead to surprising\nresults with a stateless parser. There is not much you can do if the\nnesting level cannot be determined. Using the indentation as a proxy may\nwork, but it depends on that the indentation width is agreed on in the\ncommunity.\n\nNote that the function header patterns are used for these tasks:\n\n1. For hunk headers in patch text\n2. To find the context in diff and grep --function-context\n3. To find the lines of interest in log -L:function_name:file.name\n\nFor the first, mismatching the function is secondary because it is (IMO)\njust a hint for the reader about where the hunk should apply.\n\nFor the others, finding the actual beginning and end of a function would\nbe desirable. This does not work as intended when patterns match nested\nfunction headers. But a general solution to this problem basically means\nto implement a parser for the language.\n\n> \n> 3. exports methods by assigning an anonymous function\n>   `^[\\t ]*(exports\\\\.[$_[:alpha:]][$_[:alnum:]]*[\\t ]*=[\\t ]*(\\\\(.*\\\\)|[$_[:alpha:]][$_[:alnum:]]*)[\\t ]*=>.*)`\n>    example:\n>    1. https://github.com/webpack/webpack/blob/c181294865dca01b28e6e316636fef5f2aad4eb6/lib/dependencies/DynamicExports.js#L17\n>    2. https://github.com/ajaxorg/ace/blob/d95725983b363a616c584237013dfd36eaadbba4/lib/ace/lib/dom.js#L37\n> 4. expression about assign function to LHS\n>   `^(.*=[\\t ]*function[\\t ]*([$_[:alpha:]][$_[:alnum:]]*)?[\\t ]*\\\\(.*)`\n>    example:\n>    1. https://github.com/ajaxorg/ace/blob/94422a4a892495564c56089af85019a8f8f24673/lib/ace/anchor.js#L102\n>    2. https://github.com/ajaxorg/ace/blob/d95725983b363a616c584237013dfd36eaadbba4/lib/ace/lib/dom.js#L37\n>    3. https://github.com/ajaxorg/ace/blob/4257621787b4253d6d493611f4ec5a37829da323/lib/ace/search.js#L350\n>    4. https://github.com/mozilla/pdf.js/blob/85ff7b117e04471c550914b7a13193ab7f7ecc6a/src/display/canvas.js#L299\n>    \n>    Maybe this should only match on the 0,1,2 indent level? Because JavaScript may match the function assignment in another function.\n> 5. normal function in object literal\n>   `^[\\t ]*([$_[:alpha:]][$_[:alnum:]]*[\\t ]*:[\\t ]*function[\\t ].*)`\n>     1. https://github.com/jquery/jquery/blob/de5398a6ad088dc006b46c6a870a2a053f4cd663/src/core.js#L201\n>     2. https://github.com/mozilla/pdf.js/blob/85ff7b117e04471c550914b7a13193ab7f7ecc6a/src/display/canvas.js#L242\n> 6. function in class\n>   `^[\\t ]*((static[\\t ]+)?((async|get|set)[\\t ]+)?[$_[:alpha:]][$_[:alnum:]]*[\\t ]*\\\\(.*)`\n>     \n>    This regex is tricky because the class's function is no function keyword in JavaScript. \n>    If you write the regex to match them, it will match many non-function declaration things!!! \n>    Like examples below:\n>    1. the non-function matches\n>      1. https://github.com/ajaxorg/ace/blob/94422a4a892495564c56089af85019a8f8f24673/lib/ace/anchor.js#L58\n>      2. https://github.com/ajaxorg/ace/blob/d95725983b363a616c584237013dfd36eaadbba4/lib/ace/lib/dom.js#L132\n>    2. the function in class\n>      1. https://github.com/mozilla/pdf.js/blob/85ff7b117e04471c550914b7a13193ab7f7ecc6a/src/display/canvas.js#L1929\n>      2. https://github.com/webpack/webpack/blob/ccecc17c01af96edddb931a76e7a3b21ef2969d8/lib/Chunk.js#L179\n>      3. https://github.com/webpack/webpack/blob/612de998f186a9bb2fe8769a91678df689a0541e/lib/Module.js#L242\n>      4. https://github.com/mozilla/pdf.js/blob/5cf116a958548f6596674bf8d5ca0fe64aa2df3c/web/view_history.js#L75\n>      5. https://github.com/mozilla/pdf.js/blob/5cf116a958548f6596674bf8d5ca0fe64aa2df3c/web/view_history.js#L89\n>    \n>     My solution is to add some negation rules, and one rule is skipping the keywords that may have characters immediately \n>     following them in the parenthesis, rule is `!^[ \\t]*(if|do|while|for|with|switch|catch|import|return)`.\n>     Another negation rule is only before this 「function in class」 regex, that skips the line's indent level more than \n>     one because most of the function in class has one indent level(the class is on top-level). The negation rule is \n>     `!^(\\t{2,}|[ ]{5,})`.\n>     \n>     But this is not enough, because maybe has function call on one indent level. So need an negation rule for skipping \n>     statement. The negation rule is `!^.*;[ \\t]*`. But the bad news is JavaScript's statement can end without a semicolon. \n>     So it still has an opportunity to recognize function call as the function declaration if the code's statement does not \n>     end with semicolons.\n\nThe patterns need not be perfect, but \"only\" reasonably useful, i.e.,\nsuitable for the majority of the actual codebase. If it turns out that\ntoo many lines match in practice, the patterns can be tweaked.\n\n> \n> Word's pattern for JavaScript\n> In this part, I reference the formal ECMA specification heavily[2].\n> JavaScript has some special syntax, such as numbers can be separated with an underscore for readability[3]. \n> And has BigInt literal, which is number end with a 「n」 character. So the number-related regex would be some \n> differences with another language.\n> \n> In the last, I had a fork git project on Github. And has the CI's result, the all test cases pass[4].\n> \n> [1] https://github.com/webpack/webpack/blob/2279c5a2105ea1498b83a4854919aefe1a28c553/lib/ChunkGraph.js#L279\n> [2] https://262.ecma-international.org/12.0/#sec-ecmascript-language-lexical-grammar\n> [3] https://v8.dev/features/numeric-separators\n> [4] https://github.com/a97410985/git/actions/runs/1933091300\n> \n> xing zhi jiang (1):\n>   Add a diff driver for JavaScript languages.\n> \n>  .gitignore                                    |  1 +\n>  Documentation/gitattributes.txt               |  2 +\n>  ...avascript-assignment-of-anonymous-function |  4 ++\n>  .../javascript-assignment-of-arrow-function   |  4 ++\n>  .../javascript-assignment-of-named-function   |  4 ++\n>  t/t4018/javascript-async-function             |  4 ++\n>  t/t4018/javascript-export-async-function      |  4 ++\n>  t/t4018/javascript-export-function            |  4 ++\n>  t/t4018/javascript-exports-anomyous-function  |  4 ++\n>  .../javascript-exports-anomyous-function-2    |  4 ++\n>  t/t4018/javascript-exports-function           |  4 ++\n>  t/t4018/javascript-function                   |  4 ++\n>  t/t4018/javascript-function-2                 | 10 ++++\n>  t/t4018/javascript-function-belong-to-IIFE    |  6 +++\n>  t/t4018/javascript-function-in-class          |  6 +++\n>  t/t4018/javascript-function-in-class-2        | 11 ++++\n>  t/t4018/javascript-function-in-object-literal |  7 +++\n>  t/t4018/javascript-generator-function         |  4 ++\n>  t/t4018/javascript-generator-function-2       |  4 ++\n>  t/t4018/javascript-getter-function-in-class   |  6 +++\n>  t/t4018/javascript-setter-function-in-class   |  6 +++\n>  .../javascript-skip-function-call-statement   |  7 +++\n>  t/t4018/javascript-skip-keywords              | 34 ++++++++++++\n>  t/t4018/javascript-static-function-in-class   |  6 +++\n>  t/t4034-diff-words.sh                         |  1 +\n>  t/t4034/javascript/expect                     | 52 +++++++++++++++++++\n>  t/t4034/javascript/post                       | 32 ++++++++++++\n>  t/t4034/javascript/pre                        | 32 ++++++++++++\n>  userdiff.c                                    | 38 ++++++++++++++\n>  29 files changed, 305 insertions(+)\n>  create mode 100644 t/t4018/javascript-assignment-of-anonymous-function\n>  create mode 100644 t/t4018/javascript-assignment-of-arrow-function\n>  create mode 100644 t/t4018/javascript-assignment-of-named-function\n>  create mode 100644 t/t4018/javascript-async-function\n>  create mode 100644 t/t4018/javascript-export-async-function\n>  create mode 100644 t/t4018/javascript-export-function\n>  create mode 100644 t/t4018/javascript-exports-anomyous-function\n>  create mode 100644 t/t4018/javascript-exports-anomyous-function-2\n>  create mode 100644 t/t4018/javascript-exports-function\n>  create mode 100644 t/t4018/javascript-function\n>  create mode 100644 t/t4018/javascript-function-2\n>  create mode 100644 t/t4018/javascript-function-belong-to-IIFE\n>  create mode 100644 t/t4018/javascript-function-in-class\n>  create mode 100644 t/t4018/javascript-function-in-class-2\n>  create mode 100644 t/t4018/javascript-function-in-object-literal\n>  create mode 100644 t/t4018/javascript-generator-function\n>  create mode 100644 t/t4018/javascript-generator-function-2\n>  create mode 100644 t/t4018/javascript-getter-function-in-class\n>  create mode 100644 t/t4018/javascript-setter-function-in-class\n>  create mode 100644 t/t4018/javascript-skip-function-call-statement\n>  create mode 100644 t/t4018/javascript-skip-keywords\n>  create mode 100644 t/t4018/javascript-static-function-in-class\n>  create mode 100644 t/t4034/javascript/expect\n>  create mode 100644 t/t4034/javascript/post\n>  create mode 100644 t/t4034/javascript/pre\n> \n> \n> base-commit: e6ebfd0e8cbbd10878070c8a356b5ad1b3ca464e\n\n-- Hannes\n"},{"id":"450585","messageId":"CAN_FRNK-FD+4_Nf_VdmU1N+TBapQ7ZnWo5Bn8M9YDWWhMfBypg@mail.gmail.com","threadId":"57508","inReplyTo":"79bd770e-74f1-edf8-884b-c0f916732ce0@kdbg.org","subject":"Re: [GSoC][PATCH 1/1] Add a diff driver for JavaScript languages.","fromName":"xing-zhi jiang","fromEmail":"a97410985new@gmail.com","sentAt":"2022-03-07T15:10:11Z","receivedAt":"2022-03-07T15:10:55Z","isPatch":true,"sender":{"key":"a97410985new@gmail.com","avatar":"https://gravatar.com/avatar/a66532d6438c89cb1989916e4a054b2c0c069296379adef42f9fbfca6afa6b06?d=mp&s=160"},"body":"Thanks for your review and give much good advice !!!\nI will apply much of your advice to my v2 patch. But before I write\nthe v2 patch,\nthere is still something to check and discuss.\n\nOn Sat, 5 Mar 2022 at 18:16, Johannes Sixt <j6t@kdbg.org> wrote:\n>\n> Thank you for your contribution!\n>\n> Am 04.03.22 um 14:08 schrieb xing zhi jiang:\n> > In the xfunction part that matches normal functions,\n> > a variable declaration with an assignment of function, the function declaration\n> > in the class, and also the function is object literal's property.\n>\n> On the first read, I stumbled over the last half-sentence. Is this\n> JavaScript-lingo for the construct\n>\n>    x = { foo: function() { ... } }\n>\n> A parenthetical note in this regard would be helpful.\n>\n> > And in the word regex part, that matches numbers, punctuations, and also the\n> > JavaScript identifier.\n> > This part heavily references the formal ECMA sepcification[1].\n> >\n> > [1]https://262.ecma-international.org/12.0/#sec-ecmascript-language-lexical-grammar\n>\n> After having seen the regex, to follow the syntax specification is\n> unnecessarily tight. If you follow my suggestions, the note should\n> probably be rewritten, but keeping the link to the language reference is\n> certainly helpful.\nOK, I will adjust the commit message but remain the reference link.\n\n> >\n> > Signed-off-by: xing zhi jiang <a97410985new@gmail.com>\n> > ---\n> >  .gitignore                                    |  1 +\n> >  Documentation/gitattributes.txt               |  2 +\n> >  ...avascript-assignment-of-anonymous-function |  4 ++\n> >  .../javascript-assignment-of-arrow-function   |  4 ++\n> >  .../javascript-assignment-of-named-function   |  4 ++\n> >  t/t4018/javascript-async-function             |  4 ++\n> >  t/t4018/javascript-export-async-function      |  4 ++\n> >  t/t4018/javascript-export-function            |  4 ++\n> >  t/t4018/javascript-exports-anomyous-function  |  4 ++\n> >  .../javascript-exports-anomyous-function-2    |  4 ++\n> >  t/t4018/javascript-exports-function           |  4 ++\n> >  t/t4018/javascript-function                   |  4 ++\n> >  t/t4018/javascript-function-2                 | 10 ++++\n> >  t/t4018/javascript-function-belong-to-IIFE    |  6 +++\n> >  t/t4018/javascript-function-in-class          |  6 +++\n> >  t/t4018/javascript-function-in-class-2        | 11 ++++\n> >  t/t4018/javascript-function-in-object-literal |  7 +++\n> >  t/t4018/javascript-generator-function         |  4 ++\n> >  t/t4018/javascript-generator-function-2       |  4 ++\n> >  t/t4018/javascript-getter-function-in-class   |  6 +++\n> >  t/t4018/javascript-setter-function-in-class   |  6 +++\n> >  .../javascript-skip-function-call-statement   |  7 +++\n> >  t/t4018/javascript-skip-keywords              | 34 ++++++++++++\n> >  t/t4018/javascript-static-function-in-class   |  6 +++\n> >  t/t4034-diff-words.sh                         |  1 +\n> >  t/t4034/javascript/expect                     | 52 +++++++++++++++++++\n> >  t/t4034/javascript/post                       | 32 ++++++++++++\n> >  t/t4034/javascript/pre                        | 32 ++++++++++++\n> >  userdiff.c                                    | 38 ++++++++++++++\n> >  29 files changed, 305 insertions(+)\n> >  create mode 100644 t/t4018/javascript-assignment-of-anonymous-function\n> >  create mode 100644 t/t4018/javascript-assignment-of-arrow-function\n> >  create mode 100644 t/t4018/javascript-assignment-of-named-function\n> >  create mode 100644 t/t4018/javascript-async-function\n> >  create mode 100644 t/t4018/javascript-export-async-function\n> >  create mode 100644 t/t4018/javascript-export-function\n> >  create mode 100644 t/t4018/javascript-exports-anomyous-function\n> >  create mode 100644 t/t4018/javascript-exports-anomyous-function-2\n> >  create mode 100644 t/t4018/javascript-exports-function\n> >  create mode 100644 t/t4018/javascript-function\n> >  create mode 100644 t/t4018/javascript-function-2\n> >  create mode 100644 t/t4018/javascript-function-belong-to-IIFE\n> >  create mode 100644 t/t4018/javascript-function-in-class\n> >  create mode 100644 t/t4018/javascript-function-in-class-2\n> >  create mode 100644 t/t4018/javascript-function-in-object-literal\n> >  create mode 100644 t/t4018/javascript-generator-function\n> >  create mode 100644 t/t4018/javascript-generator-function-2\n> >  create mode 100644 t/t4018/javascript-getter-function-in-class\n> >  create mode 100644 t/t4018/javascript-setter-function-in-class\n> >  create mode 100644 t/t4018/javascript-skip-function-call-statement\n> >  create mode 100644 t/t4018/javascript-skip-keywords\n> >  create mode 100644 t/t4018/javascript-static-function-in-class\n> >  create mode 100644 t/t4034/javascript/expect\n> >  create mode 100644 t/t4034/javascript/post\n> >  create mode 100644 t/t4034/javascript/pre\n> >\n> > diff --git a/.gitignore b/.gitignore\n> > index f817c509ec..de628c53f8 100644\n> > --- a/.gitignore\n> > +++ b/.gitignore\n> > @@ -244,3 +244,4 @@ Release/\n> >  /git.VC.db\n> >  *.dSYM\n> >  /contrib/buildsystems/out\n> > +/.cache\n> > \\ No newline at end of file\n>\n> Do not include this change. It does not belong to this commit.\nOK\n\n> > diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\n> > index 60984a4682..f6554a4651 100644\n> > --- a/Documentation/gitattributes.txt\n> > +++ b/Documentation/gitattributes.txt\n> > @@ -828,6 +828,8 @@ patterns are available:\n> >\n> >  - `java` suitable for source code in the Java language.\n> >\n> > +- `javascript suitable for source code in the JavaScript language.\n>\n> Please do not forget the closing quote.\nI will fix it in v2 patch.\n\n> > +\n> >  - `markdown` suitable for Markdown documents.\n> >\n> >  - `matlab` suitable for source code in the MATLAB and Octave languages.\n> > diff --git a/t/t4018/javascript-assignment-of-anonymous-function b/t/t4018/javascript-assignment-of-anonymous-function\n> > new file mode 100644\n> > index 0000000000..5d4701e84c\n> > --- /dev/null\n> > +++ b/t/t4018/javascript-assignment-of-anonymous-function\n> > @@ -0,0 +1,4 @@\n> > +const RIGHT = function (a, b) {\n> > +\n> > +    return a + b; // ChangeMe\n> > +};\n> > \\ No newline at end of file\n>\n> Notice this \"No newline at end of file\". Please complete the last line\n> of the file, i.e. do add the last line break. Same for all other new\n> files introduced in this patch.\nI will fix it in v2 patch.\n\n> > diff --git a/t/t4018/javascript-assignment-of-arrow-function b/t/t4018/javascript-assignment-of-arrow-function\n> > new file mode 100644\n> > index 0000000000..6ab73ccb7a\n> > --- /dev/null\n> > +++ b/t/t4018/javascript-assignment-of-arrow-function\n> > @@ -0,0 +1,4 @@\n> > +const RIGHT = (a, b) => {\n> > +\n> > +    return a + b; // ChangeMe\n> > +};\n> > \\ No newline at end of file\n> > diff --git a/t/t4018/javascript-assignment-of-named-function b/t/t4018/javascript-assignment-of-named-function\n> > new file mode 100644\n> > index 0000000000..85d43123a6\n> > --- /dev/null\n> > +++ b/t/t4018/javascript-assignment-of-named-function\n> > @@ -0,0 +1,4 @@\n> > +const RIGHT = function test (a, b) {\n> > +\n> > +    return a + b; // ChangeMe\n> > +};\n> > \\ No newline at end of file\n> > diff --git a/t/t4018/javascript-async-function b/t/t4018/javascript-async-function\n> > new file mode 100644\n> > index 0000000000..ebc7c8c05b\n> > --- /dev/null\n> > +++ b/t/t4018/javascript-async-function\n> > @@ -0,0 +1,4 @@\n> > +async function RIGHT(a, b) {\n> > +\n> > +  return a + b; // ChangeMe\n> > +}\n> > \\ No newline at end of file\n> > diff --git a/t/t4018/javascript-export-async-function b/t/t4018/javascript-export-async-function\n> > new file mode 100644\n> > index 0000000000..3cd60b7980\n> > --- /dev/null\n> > +++ b/t/t4018/javascript-export-async-function\n> > @@ -0,0 +1,4 @@\n> > +export async function RIGHT(a, b) {\n> > +\n> > +  return a + b; // ChangeMe\n> > +}\n> > \\ No newline at end of file\n> > diff --git a/t/t4018/javascript-export-function b/t/t4018/javascript-export-function\n> > new file mode 100644\n> > index 0000000000..177ddec779\n> > --- /dev/null\n> > +++ b/t/t4018/javascript-export-function\n> > @@ -0,0 +1,4 @@\n> > +export function RIGHT(a, b) {\n> > +\n> > +  return a + b; // ChangeMe\n> > +}\n> > \\ No newline at end of file\n> > diff --git a/t/t4018/javascript-exports-anomyous-function b/t/t4018/javascript-exports-anomyous-function\n> > new file mode 100644\n> > index 0000000000..45b0ecd659\n> > --- /dev/null\n> > +++ b/t/t4018/javascript-exports-anomyous-function\n> > @@ -0,0 +1,4 @@\n> > +exports.setFlagged = RIGHT => {\n> > +\n> > +    return ChangeMe;\n> > +};\n> > \\ No newline at end of file\n> > diff --git a/t/t4018/javascript-exports-anomyous-function-2 b/t/t4018/javascript-exports-anomyous-function-2\n> > new file mode 100644\n> > index 0000000000..0c572bfde3\n> > --- /dev/null\n> > +++ b/t/t4018/javascript-exports-anomyous-function-2\n> > @@ -0,0 +1,4 @@\n> > +exports.RIGHT = (a, b, runtime) => {\n> > +\n> > +    return ChangeMe;\n> > +};\n> > \\ No newline at end of file\n> > diff --git a/t/t4018/javascript-exports-function b/t/t4018/javascript-exports-function\n> > new file mode 100644\n> > index 0000000000..f1587fddac\n> > --- /dev/null\n> > +++ b/t/t4018/javascript-exports-function\n> > @@ -0,0 +1,4 @@\n> > +exports.RIGHT = function(document) {\n> > +\n> > +    return ChangeMe\n> > +}\n>\n> Not a particularly important point, but the correct syntax requires a\n> semicolon here, I guess.\n>\nYes, I forgot the semicolon here.\n\n> > +                                     delete $installedChunks$[chunkId];\n> > +                             });\n> > +                             currentUpdateRemovedChunks = ChangeMe;\n> > +   }\n> > +  }\n> > +}\n> > \\ No newline at end of file\n> > diff --git a/t/t4018/javascript-function-belong-to-IIFE b/t/t4018/javascript-function-belong-to-IIFE\n> > new file mode 100644\n> > index 0000000000..6a80118e8a\n> > --- /dev/null\n> > +++ b/t/t4018/javascript-function-belong-to-IIFE\n> > @@ -0,0 +1,6 @@\n> > +(function () {\n> > +  this.$RIGHT = function (needle, modifier) {\n> > +      let a = 5;\n> > +      return ChangeMe;\n> > +  };\n> > +}).call(aaaa.prototype);\n> > \\ No newline at end of file\n> > diff --git a/t/t4018/javascript-function-in-class b/t/t4018/javascript-function-in-class\n> > new file mode 100644\n> > index 0000000000..4b2f9c37e0\n> > --- /dev/null\n> > +++ b/t/t4018/javascript-function-in-class\n> > @@ -0,0 +1,6 @@\n> > +class Test {\n> > +  RIGHT() {\n> > +    let a = 4;\n> > +    let b = ChangeMe;\n> > +  }\n> > +}\n> > \\ No newline at end of file\n> > diff --git a/t/t4018/javascript-function-in-class-2 b/t/t4018/javascript-function-in-class-2\n> > new file mode 100644\n> > index 0000000000..402c4aecc3\n> > --- /dev/null\n> > +++ b/t/t4018/javascript-function-in-class-2\n> > @@ -0,0 +1,11 @@\n> > +class Test {\n> > +  RIGHT(\n> > +      aaaaaaaaaa,\n> > +      bbbbbbbbbb,\n> > +      cccccccccc,\n> > +      dddddddddd\n> > +  ) {\n> > +    let a = 4;\n> > +    let b = ChangeMe;\n> > +  }\n> > +}\n> > \\ No newline at end of file\n>\n> In the above two, we see class member functions. Is there a test case\n> where the function parameter is on the same line as the function name or\n> is that one of the difficult cases?\nI can't get what you mean, but the second Test case wants to make sure we\ncan match the function with a long parameter list, and its parameters are\nformatted to multiline.\n\n> > diff --git a/t/t4034/javascript/post b/t/t4034/javascript/post\n> > new file mode 100644\n> > index 0000000000..8bc3e3af12\n> > --- /dev/null\n> > +++ b/t/t4034/javascript/post\n> > @@ -0,0 +1,32 @@\n> > +// DecimalLiteral\n> > +124\n> > +0.124\n> > +0.123e-5\n> > +0.123E-5\n> > +0.123E5\n> > +12222222222222222233344445n\n> > +// HexIntegerLiteral\n> > +0x11\n> > +0X5Fa1\n> > +0x123_756\n> > +0x1234182989812f1289bn\n> > +// OctalIntegerLiteral\n> > +06\n> > +0o5\n> > +0O4\n> > +0511_567\n> > +0o424242424242424242424242424242666667n\n> > +// BinaryIntegerLiteral\n> > +0b1101\n> > +0B0010\n> > +0b0001_1101_0011\n> > +0b11111111111111000011111111111111111n\n> > +// punctuations\n> > +{b} (b)\n> > +b;\n> > +[2,3]\n> > +[ ...params_v2 ]\n> > +a=2 a=2 a=2 a=2 a=2 a=2 a=2 a=2\n> > +a-=b a%=b a&&=b a|=b\n> > +b-c a++ a<<b a>>>=b a<<=b\n> > +a&b a|b a??=b\n> > \\ No newline at end of file\n> > diff --git a/t/t4034/javascript/pre b/t/t4034/javascript/pre\n> > new file mode 100644\n> > index 0000000000..b72238f779\n> > --- /dev/null\n> > +++ b/t/t4034/javascript/pre\n> > @@ -0,0 +1,32 @@\n> > +// DecimalLiteral\n> > +123\n> > +0.123\n> > +0.123e+5\n> > +0.123E+5\n> > +0.123e5\n> > +1222222222222222223334444n\n> > +// HexIntegerLiteral\n> > +0x10\n> > +0X6Fa1\n> > +0x123_456\n> > +0x1234182989812f1289an\n> > +// OctalIntegerLiteral\n> > +05\n> > +0o6\n> > +0O7\n> > +0512_567\n> > +0o424242424242424242424242424242666666n\n> > +// BinaryIntegerLiteral\n> > +0b1001\n> > +0B0110\n> > +0b0001_1001_0011\n> > +0b1111111111111111111111111111111111111n\n> > +// punctuations\n> > +{a} (a)\n> > +a;\n> > +[1,2]\n> > +[ 1, 2, ...params ]\n> > +a<=2 a>=2 a==2 a!=2 a===2 a!==2 a^=2 a=>2\n> > +a+=b a*=b a**=b a||=b\n> > +b+c a-- a>>b a>>>b a>>=b\n> > +a&&b a||b a&&=b\n> > \\ No newline at end of file\n> > diff --git a/userdiff.c b/userdiff.c\n> > index 8578cb0d12..a6a341e3c1 100644\n> > --- a/userdiff.c\n> > +++ b/userdiff.c\n> > @@ -168,6 +168,44 @@ PATTERNS(\"java\",\n> >        \"|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?\"\n> >        \"|[-+*/<>%&^|=!]=\"\n> >        \"|--|\\\\+\\\\+|<<=?|>>>?=?|&&|\\\\|\\\\|\"),\n> > +\n> > +PATTERNS(\"javascript\",\n> > +      /* don't match the expression may contain parenthesis, because it is not a function declaration */\n> > +      \"!^[ \\t]*(if|do|while|for|with|switch|catch|import|return)\\n\"\n>\n> These will not match\n>\n>         }while (expr)\n>\n> note the absent blank before the keyword, but that is an acceptable\n> trade-off to keep things simple. Good.\n>\nI thought not to match with `}while (expr)` is OK because this rule is\nmainly to prevent matching too many\n wrong things about the regex for 「the function in the class」.\nAnd before keywords maybe need blanks, because some javascript\nformatting style is using space\ninstead of tab.\n\n\n> > +      /* don't match statement */\n> > +      \"!^.*;[ \\t]*\\n\"\n>\n> This regexp can be reduced to\n>\n>         \"!;\\n\"\n>\ngood advice, I would apply it.\n\n> > +      /* match normal function */\n> > +      \"^[\\t ]*((export[\\t ]+)?(async[\\t ]+)?function[\\t ]*([\\t ]*\\\\*[\\t ]*|[\\t ]*)?[$_[:alpha:]][$_[:alnum:]]*[\\t ]*\\\\(.*)\\n\"\n>\n> Good. One note though: keyword \"function\" can optionally be followed by\n> an asterisk '*'. You can probably simplify the middle part to\n>\n>         ...function[\\t *]*...identifier...\ngood advice, I would apply it.\n\n> > +      /* match JavaScript variable declaration with a lambda expression */\n> > +      \"^[\\t ]*((const|let|var)[\\t ]*[$_[:alpha:]][$_[:alnum:]]*[\\t ]*=[\\t ]*\"\n> > +      \"(\\\\(.*\\\\)|[$_[:alpha:]][$_[:alnum:]]*[\\t ])[\\t ]*=>[\\t ]*\\\\{?)\\n\"\n>\n> Let's break this down:\n>\n>         \"^[\\t ]*\"\n>             \"(\"\n>                 \"(const|let|var)[\\t ]*\"\n>                 \"[$_[:alpha:]][$_[:alnum:]]*[\\t ]*\"\n>                 \"=[\\t ]*\"\n>                 \"(\"\n>                     \"\\\\(.*\\\\)\"\n>                     \"|\"\n>                     \"[$_[:alpha:]][$_[:alnum:]]*[\\t ]\"\n>                 \")[\\t ]*\"\n>                 \"=>[\\t ]*\"\n>                 \"\\\\{?\"\n>             \")\\n\"\n>\n> Can you not have\n>\n>         var f = foo=>{\n>\n> because I see that whitespace is required between the identifier and \"=>\"?\nIt is my mistake. I should not match tab or space between identifier and =>.\nThanks for pointing it out. I will fix it.\n\n> > +      /* match exports for anonymous fucntion */\n> > +      \"^[\\t ]*(exports\\\\.[$_[:alpha:]][$_[:alnum:]]*[\\t ]*=[\\t ]*(\\\\(.*\\\\)|[$_[:alpha:]][$_[:alnum:]]*)[\\t ]*=>.*)\\n\"\n>\n> Here, whitespace is not required. Is the above an oversight?\n>\n> BTW, can keyword \"exports\" be used for something other than functions?\n>\n「Whitespace is not required」 is only referred to 「^[\\t ]*(exports」? I\nwould remove the [\\t ]* before exports\nkeyword because exports must be top-level.\nThe exports keyword can be used for exporting many things, such as\nstring. ex: exports.SimpleMessage = 'Hello world';\n, So I define a more precise regex for only matching anonymous functions.\n\n> > +      /* match assign function to LHS */\n> > +      \"^(.*=[\\t ]*function[\\t ]*([$_[:alpha:]][$_[:alnum:]]*)?[\\t ]*\\\\(.*)\\n\"\n>\n> This makes me think that whenever the keyword \"function\" appears, then we\n> see the beginning of a function. This would allow a simple pattern\n> upfront that picks out all functions defined with this keyword, and all\n> other patterns need only be concerned with the exceptional cases.\n>\n>         /* \"function\" is first non-space token */\n>         \"^[\\t ]*function[\\t ].*)\\n\"\n>         /* \"function\" is not first token */\n>         \"^.*[^$_[:alnum:]]function[\\t ].*\\n\"\nThis is good advice, I would try.\n\n> > +      /* match normal function in object literal */\n> > +      \"^[\\t ]*([$_[:alpha:]][$_[:alnum:]]*[\\t ]*:[\\t ]*function[\\t ].*)\\n\"\n> > +      /* don't match the function in class, which has more than one ident level */\n> > +      \"!^(\\t{2,}|[ ]{5,})\\n\"\n>\n> For some, hopefully universally agreed upon in the JavaScript community,\n> definition of indentation level. Ok...\nI had searched the web, the javascript's style about indentation level\nhas two kinds -\nident with 2 or 4 spaces. So I don't match the line, which begins with\nmore than four\nspaces. If javascript must end with a semicolon, it doesn't need this rule :(\n\n> > +      /* match function in class */\n> > +      \"^[\\t ]*((static[\\t ]+)?((async|get|set)[\\t ]+)?[$_[:alpha:]][$_[:alnum:]]*[\\t ]*\\\\(.*)\",\n>\n> Aren't \"get\" and \"set\" as universal identifiers of functions or can they\n> occur on other contexts? Thinking of it, they can occur in comments\n> everywhere, so they would pick up too many matches if treated like my\n> \"function\" proposal above.\n\"get\" and \"set\" are not javascript keywords, we could write code like\n\"let get = 5;\"\nIn my view, it wouldn't match the comment, because the comment has \"//\" or \"/*.\nBut I would think about it.\n\n> > +      /* word regex */\n> > +      /* hexIntegerLiteral and bigHexIntegerLiteral*/\n> > +      \"0[xX][0-9a-fA-F][_0-9a-fA-F]*n?|\"\n> > +      /* octalIntegerLiteral and bigOctalIntegerLiteral */\n> > +      \"0[oO]?[0-7][_0-7]*n?|\"\n> > +      /* binaryIntegerLiteral and bigBinaryIntegerLiteral */\n> > +      \"(0[bB][01][_01]*n?)|\"\n> > +      /* decimalLiteral */\n> > +      \"(0|[1-9][_0-9]*)?\\\\.?[0-9][_0-9]*([eE][+-]?[_0-9]+)?|\"\n> > +      /* bigDecimalLiteral */\n> > +      \"(0|[1-9][_0-9]*)n|\"\n>\n> You do not have to make the word-regex so tight that it excludes\n> incorrect literals because you can assume that incorrect literals will\n> not occur. In particular integers beginning with a 0 need not be treated\n> specially. You can fold the octal and decimal integers into a single\n> expression:\n>\n>         \"[0-9][oO]?[_0-9.]*)([eE][+-]?[_0-9]+)?n?\"\n>\n> Are floatingpoint literals beginning with a decimal point like .5 not\n> permitted?\nThis is permitted. ex: \"let a = .5\" is valid.\nAnd I would follow your advice to combine multiple regexes.\n\n> Please follow the custom to place the alternation character \"|\" at the\n> beginning of the next line, not at the end of the previous.\nOK.\n\n> > +      /* punctuations */\n> > +      \"\\\\{|\\\\}|\\\\(|\\\\)|\\\\.|\\\\.{3}|;|,|<|>|<=|>=|==|!=|={3}|!==|\\\\+|-|\\\\*|/|%|\\\\*{2}|\"\n> > +      \"\\\\+{2}|--|<<|>>|>>>|&|\\\\||\\\\^|!|~|&&|\\\\|{2}|\\\\?{1,2}|:|=|\\\\+=|-=|\\\\*=|%=|\\\\*{2}=|\"\n> > +      \"<<=|>>=|>>>=|&=|\\\\|=|\\\\^=|&&=|\\\\|{2}=|\\\\?{2}=|=>|\"\n>\n> You could collaps many of the operators into single alternatives, but if\n> you prefer it this way, it is fine, too. One example:\n>\n>         \">{1,3}=\"\n>\n> But please remove the single-character operators from the list, because\n> there is an implicit single-character alternative that is not visible in\n> the code here.\n>\n> > +      /* identifiers */\n> > +      \"[$_[:alpha:]][$_[:alnum:]]*\"),\n> >  PATTERNS(\"markdown\",\n> >        \"^ {0,3}#{1,6}[ \\t].*\",\n> >        /* -- */\n>\n> -- Hannes\nOK.\n\nIn the end, I am very much grateful for your review.\n\nBest regards.\n"},{"id":"450702","messageId":"504f9f0d-a1ab-5ce7-d744-9398b0e7e68a@kdbg.org","threadId":"57508","inReplyTo":"CAN_FRNK-FD+4_Nf_VdmU1N+TBapQ7ZnWo5Bn8M9YDWWhMfBypg@mail.gmail.com","subject":"Re: [GSoC][PATCH 1/1] Add a diff driver for JavaScript languages.","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2022-03-08T06:46:59Z","receivedAt":"2022-03-08T06:47:04Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 07.03.22 um 16:10 schrieb xing-zhi jiang:\n> On Sat, 5 Mar 2022 at 18:16, Johannes Sixt <j6t@kdbg.org> wrote:\n>> Am 04.03.22 um 14:08 schrieb xing zhi jiang:\n>>> diff --git a/t/t4018/javascript-function-in-class-2 b/t/t4018/javascript-function-in-class-2\n>>> new file mode 100644\n>>> index 0000000000..402c4aecc3\n>>> --- /dev/null\n>>> +++ b/t/t4018/javascript-function-in-class-2\n>>> @@ -0,0 +1,11 @@\n>>> +class Test {\n>>> +  RIGHT(\n>>> +      aaaaaaaaaa,\n>>> +      bbbbbbbbbb,\n>>> +      cccccccccc,\n>>> +      dddddddddd\n>>> +  ) {\n>>> +    let a = 4;\n>>> +    let b = ChangeMe;\n>>> +  }\n>>> +}\n>>> \\ No newline at end of file\n>>\n>> In the above two, we see class member functions. Is there a test case\n>> where the function parameter is on the same line as the function name or\n>> is that one of the difficult cases?\n> I can't get what you mean, but the second Test case wants to make sure we\n> can match the function with a long parameter list, and its parameters are\n> formatted to multiline.\n\nI meant a multi-line function call like so, where the first argument is\non the same line with the function name:\n\n  RIGHT(aaaaaaaaaa,\n      bbbbbbbbbb,\n      cccccccccc,\n      dddddddddd\n  ) {\n  ...\n\n>>> diff --git a/userdiff.c b/userdiff.c\n>>> index 8578cb0d12..a6a341e3c1 100644\n>>> --- a/userdiff.c\n>>> +++ b/userdiff.c\n>>> @@ -168,6 +168,44 @@ PATTERNS(\"java\",\n>>>        \"|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?\"\n>>>        \"|[-+*/<>%&^|=!]=\"\n>>>        \"|--|\\\\+\\\\+|<<=?|>>>?=?|&&|\\\\|\\\\|\"),\n>>> +\n>>> +PATTERNS(\"javascript\",\n>>> +      /* don't match the expression may contain parenthesis, because it is not a function declaration */\n>>> +      \"!^[ \\t]*(if|do|while|for|with|switch|catch|import|return)\\n\"\n>>\n>> These will not match\n>>\n>>         }while (expr)\n>>\n>> note the absent blank before the keyword, but that is an acceptable\n>> trade-off to keep things simple. Good.\n>>\n> I thought not to match with `}while (expr)` is OK because this rule is\n> mainly to prevent matching too many\n>  wrong things about the regex for 「the function in the class」.\n> And before keywords maybe need blanks, because some javascript\n> formatting style is using space\n> instead of tab.\n\nI don't recall why I emphasize the absent blank between '}' and \"while\".\nI must have misread something. At any rate, the regex would not match,\neither, if there is whitespace between the two, so, it is totally OK.\n\n>> Can you not have\n>>\n>>         var f = foo=>{\n>>\n>> because I see that whitespace is required between the identifier and \"=>\"?\n> It is my mistake. I should not match tab or space between identifier and =>.\n> Thanks for pointing it out. I will fix it.\n> \n>>> +      /* match exports for anonymous fucntion */\n>>> +      \"^[\\t ]*(exports\\\\.[$_[:alpha:]][$_[:alnum:]]*[\\t ]*=[\\t ]*(\\\\(.*\\\\)|[$_[:alpha:]][$_[:alnum:]]*)[\\t ]*=>.*)\\n\"\n>>\n>> Here, whitespace is not required. Is the above an oversight?\n>>\n>> BTW, can keyword \"exports\" be used for something other than functions?\n>>\n> 「Whitespace is not required」 is only referred to 「^[\\t ]*(exports」?\n\nNo, I refer to the whitespace around \"=>\" that is not required here, but\nwas required in the previous expression.\n\n> I\n> would remove the [\\t ]* before exports\n> keyword because exports must be top-level.\n> The exports keyword can be used for exporting many things, such as\n> string. ex: exports.SimpleMessage = 'Hello world';\n> , So I define a more precise regex for only matching anonymous functions.\n\nUnderstood. \"exports\" is a keyword only in certain contexts. Since it\ncan be used as (or like?) a variable name, it cannot be used as match\nthat always identifies a function header.\n\n-- Hannes\n"},{"id":"451221","messageId":"20220312164803.57909-1-a97410985new@gmail.com","threadId":"57508","inReplyTo":"20220304130854.857746-1-a97410985new@gmail.com","subject":"[GSoC][PATCH v2] Add a diff driver for JavaScript languages.","fromName":"xing zhi jiang","fromEmail":"a97410985new@gmail.com","sentAt":"2022-03-12T16:48:03Z","receivedAt":"2022-03-12T16:55:06Z","isPatch":true,"sender":{"key":"a97410985new@gmail.com","avatar":"https://gravatar.com/avatar/a66532d6438c89cb1989916e4a054b2c0c069296379adef42f9fbfca6afa6b06?d=mp&s=160"},"body":"In the xfunction part that matches normal functions,\na variable declaration with an assignment of function, the function declaration\nin the class, and also the function is object literal's property[1].\n\nAnd in the word regex part, that matches numbers, punctuations, and also the\nJavaScript identifier.\nThis part reference the formal ECMA specification[2].\n\n[1]https://github.com/jquery/jquery/blob/de5398a6ad088dc006b46c6a870a2a053f4cd663/src/core.js#L201\n[2]https://262.ecma-international.org/12.0/#sec-ecmascript-language-lexical-grammar\n\nSigned-off-by: xing zhi jiang <a97410985new@gmail.com>\n---\nRange-diff against v1:\n1:  7f764f97cf ! 1:  3b326bd2b6 Add a diff driver for JavaScript languages.\n    @@ Commit message\n     \n         In the xfunction part that matches normal functions,\n         a variable declaration with an assignment of function, the function declaration\n    -    in the class, and also the function is object literal's property.\n    +    in the class, and also the function is object literal's property[1].\n     \n         And in the word regex part, that matches numbers, punctuations, and also the\n         JavaScript identifier.\n    -    This part heavily references the formal ECMA sepcification[1].\n    +    This part reference the formal ECMA specification[2].\n     \n    -    [1]https://262.ecma-international.org/12.0/#sec-ecmascript-language-lexical-grammar\n    +    [1]https://github.com/jquery/jquery/blob/de5398a6ad088dc006b46c6a870a2a053f4cd663/src/core.js#L201\n    +    [2]https://262.ecma-international.org/12.0/#sec-ecmascript-language-lexical-grammar\n     \n         Signed-off-by: xing zhi jiang <a97410985new@gmail.com>\n     \n    - ## .gitignore ##\n    -@@ .gitignore: Release/\n    - /git.VC.db\n    - *.dSYM\n    - /contrib/buildsystems/out\n    -+/.cache\n    - \\ No newline at end of file\n    -\n      ## Documentation/gitattributes.txt ##\n     @@ Documentation/gitattributes.txt: patterns are available:\n      \n      - `java` suitable for source code in the Java language.\n      \n    -+- `javascript suitable for source code in the JavaScript language.\n    ++- `javascript` suitable for source code in the JavaScript language.\n     +\n      - `markdown` suitable for Markdown documents.\n      \n    @@ t/t4018/javascript-assignment-of-anonymous-function (new)\n     +\t\n     +    return a + b; // ChangeMe\n     +};\n    - \\ No newline at end of file\n     \n      ## t/t4018/javascript-assignment-of-arrow-function (new) ##\n     @@\n    @@ t/t4018/javascript-assignment-of-arrow-function (new)\n     +\t\n     +    return a + b; // ChangeMe\n     +};\n    - \\ No newline at end of file\n    +\n    + ## t/t4018/javascript-assignment-of-arrow-function-2 (new) ##\n    +@@\n    ++const RIGHT = (a, b)=>{\n    ++\t\n    ++    return a + b; // ChangeMe\n    ++};\n    +\n    + ## t/t4018/javascript-assignment-of-arrow-function-3 (new) ##\n    +@@\n    ++const RIGHT=test=>{\n    ++\t\n    ++    return test + 1; // ChangeMe\n    ++};\n     \n      ## t/t4018/javascript-assignment-of-named-function (new) ##\n     @@\n    @@ t/t4018/javascript-assignment-of-named-function (new)\n     +\t\n     +    return a + b; // ChangeMe\n     +};\n    - \\ No newline at end of file\n     \n      ## t/t4018/javascript-async-function (new) ##\n     @@\n    @@ t/t4018/javascript-async-function (new)\n     +  \n     +  return a + b; // ChangeMe\n     +}\n    - \\ No newline at end of file\n     \n      ## t/t4018/javascript-export-async-function (new) ##\n     @@\n    @@ t/t4018/javascript-export-async-function (new)\n     +  \n     +  return a + b; // ChangeMe\n     +}\n    - \\ No newline at end of file\n     \n      ## t/t4018/javascript-export-function (new) ##\n     @@\n    @@ t/t4018/javascript-export-function (new)\n     +  \n     +  return a + b; // ChangeMe\n     +}\n    - \\ No newline at end of file\n     \n      ## t/t4018/javascript-exports-anomyous-function (new) ##\n     @@\n    @@ t/t4018/javascript-exports-anomyous-function (new)\n     +\t\n     +    return ChangeMe;\n     +};\n    - \\ No newline at end of file\n     \n      ## t/t4018/javascript-exports-anomyous-function-2 (new) ##\n     @@\n    @@ t/t4018/javascript-exports-anomyous-function-2 (new)\n     +\t\n     +    return ChangeMe;\n     +};\n    - \\ No newline at end of file\n     \n      ## t/t4018/javascript-exports-function (new) ##\n     @@\n     +exports.RIGHT = function(document) {\n     +    \n    -+    return ChangeMe\n    ++    return ChangeMe;\n     +}\n    - \\ No newline at end of file\n     \n      ## t/t4018/javascript-function (new) ##\n     @@\n    @@ t/t4018/javascript-function (new)\n     +\n     +  return a + b; // ChangeMe\n     +}\n    - \\ No newline at end of file\n     \n      ## t/t4018/javascript-function-2 (new) ##\n     @@\n    @@ t/t4018/javascript-function-2 (new)\n     +   }\n     +  }\n     +}\n    - \\ No newline at end of file\n     \n      ## t/t4018/javascript-function-belong-to-IIFE (new) ##\n     @@\n    @@ t/t4018/javascript-function-belong-to-IIFE (new)\n     +      return ChangeMe;\n     +  };\n     +}).call(aaaa.prototype);\n    - \\ No newline at end of file\n     \n      ## t/t4018/javascript-function-in-class (new) ##\n     @@\n    @@ t/t4018/javascript-function-in-class (new)\n     +    let b = ChangeMe;\n     +  }\n     +}\n    - \\ No newline at end of file\n     \n      ## t/t4018/javascript-function-in-class-2 (new) ##\n     @@\n    @@ t/t4018/javascript-function-in-class-2 (new)\n     +    let b = ChangeMe;\n     +  }\n     +}\n    - \\ No newline at end of file\n    +\n    + ## t/t4018/javascript-function-in-class-3 (new) ##\n    +@@\n    ++class Test {\n    ++  RIGHT(aaaaaaaaaa,\n    ++      bbbbbbbbbb,\n    ++      cccccccccc,\n    ++      dddddddddd\n    ++  ) {\n    ++    let a = 4;\n    ++    let b = ChangeMe;\n    ++  }\n    ++}\n     \n      ## t/t4018/javascript-function-in-object-literal (new) ##\n     @@\n    @@ t/t4018/javascript-function-in-object-literal (new)\n     +        return ChangeMe\n     +    }\n     +}\n    - \\ No newline at end of file\n     \n      ## t/t4018/javascript-generator-function (new) ##\n     @@\n    @@ t/t4018/javascript-generator-function (new)\n     +  \n     +  return a + b; // ChangeMe\n     +}\n    - \\ No newline at end of file\n     \n      ## t/t4018/javascript-generator-function-2 (new) ##\n     @@\n    @@ t/t4018/javascript-generator-function-2 (new)\n     +  \n     +  return a + b; // ChangeMe\n     +}\n    - \\ No newline at end of file\n     \n      ## t/t4018/javascript-getter-function-in-class (new) ##\n     @@\n    @@ t/t4018/javascript-getter-function-in-class (new)\n     +    let b = ChangeMe;\n     +  }\n     +}\n    - \\ No newline at end of file\n     \n      ## t/t4018/javascript-setter-function-in-class (new) ##\n     @@\n    @@ t/t4018/javascript-setter-function-in-class (new)\n     +    let b = ChangeMe;\n     +  }\n     +}\n    - \\ No newline at end of file\n     \n      ## t/t4018/javascript-skip-function-call-statement (new) ##\n     @@\n    @@ t/t4018/javascript-skip-function-call-statement (new)\n     +    let b = ChangeMe;\n     +  }\n     +}\n    - \\ No newline at end of file\n     \n      ## t/t4018/javascript-skip-keywords (new) ##\n     @@\n    @@ t/t4018/javascript-skip-keywords (new)\n     +\n     +  return a + b; // ChangeMe\n     +}\n    - \\ No newline at end of file\n     \n      ## t/t4018/javascript-static-function-in-class (new) ##\n     @@\n    @@ t/t4018/javascript-static-function-in-class (new)\n     +    let b = ChangeMe;\n     +  }\n     +}\n    - \\ No newline at end of file\n     \n      ## t/t4034-diff-words.sh ##\n     @@ t/t4034-diff-words.sh: test_language_driver dts\n    @@ t/t4034-diff-words.sh: test_language_driver dts\n      ## t/t4034/javascript/expect (new) ##\n     @@\n     +<BOLD>diff --git a/pre b/post<RESET>\n    -+<BOLD>index b72238f..8bc3e3a 100644<RESET>\n    ++<BOLD>index 18f4796..46f9b62 100644<RESET>\n     +<BOLD>--- a/pre<RESET>\n     +<BOLD>+++ b/post<RESET>\n    -+<CYAN>@@ -1,32 +1,32 @@<RESET>\n    ++<CYAN>@@ -1,33 +1,33 @@<RESET>\n     +// DecimalLiteral<RESET>\n     +<RED>123<RESET>\n     +<RED>0.123<RESET>\n    ++<RED>.123<RESET>\n     +<RED>0.123e+5<RESET>\n     +<RED>0.123E+5<RESET>\n     +<RED>0.123e5<RESET>\n     +<RED>1222222222222222223334444n<RESET><GREEN>124<RESET>\n     +<GREEN>0.124<RESET>\n    ++<GREEN>.124<RESET>\n     +<GREEN>0.123e-5<RESET>\n     +<GREEN>0.123E-5<RESET>\n     +<GREEN>0.123E5<RESET>\n    @@ t/t4034/javascript/post (new)\n     +// DecimalLiteral\n     +124\n     +0.124\n    ++.124\n     +0.123e-5\n     +0.123E-5\n     +0.123E5\n    @@ t/t4034/javascript/post (new)\n     +a-=b a%=b a&&=b a|=b\n     +b-c a++ a<<b a>>>=b a<<=b\n     +a&b a|b a??=b\n    - \\ No newline at end of file\n     \n      ## t/t4034/javascript/pre (new) ##\n     @@\n     +// DecimalLiteral\n     +123\n     +0.123\n    ++.123\n     +0.123e+5\n     +0.123E+5\n     +0.123e5\n    @@ t/t4034/javascript/pre (new)\n     +a+=b a*=b a**=b a||=b\n     +b+c a-- a>>b a>>>b a>>=b\n     +a&&b a||b a&&=b\n    - \\ No newline at end of file\n     \n      ## userdiff.c ##\n     @@ userdiff.c: PATTERNS(\"java\",\n    @@ userdiff.c: PATTERNS(\"java\",\n     +\t /* don't match the expression may contain parenthesis, because it is not a function declaration */\n     +\t \"!^[ \\t]*(if|do|while|for|with|switch|catch|import|return)\\n\"\n     +\t /* don't match statement */\n    -+\t \"!^.*;[ \\t]*\\n\"\n    ++\t \"!;\\n\"\n     +\t /* match normal function */\n    -+\t \"^[\\t ]*((export[\\t ]+)?(async[\\t ]+)?function[\\t ]*([\\t ]*\\\\*[\\t ]*|[\\t ]*)?[$_[:alpha:]][$_[:alnum:]]*[\\t ]*\\\\(.*)\\n\"\n    ++\t \"^((export[\\t ]+)?(async[\\t ]+)?function[\\t ]*[\\t *]*[$_[:alpha:]][$_[:alnum:]]*[\\t ]*\\\\(.*)\\n\"\n     +\t /* match JavaScript variable declaration with a lambda expression */\n     +\t \"^[\\t ]*((const|let|var)[\\t ]*[$_[:alpha:]][$_[:alnum:]]*[\\t ]*=[\\t ]*\"\n    -+\t \"(\\\\(.*\\\\)|[$_[:alpha:]][$_[:alnum:]]*[\\t ])[\\t ]*=>[\\t ]*\\\\{?)\\n\"\n    ++\t \"(\\\\(.*\\\\)|[$_[:alpha:]][$_[:alnum:]]*)[\\t ]*=>[\\t ]*\\\\{?)\\n\"\n     +\t /* match exports for anonymous fucntion */\n    -+\t \"^[\\t ]*(exports\\\\.[$_[:alpha:]][$_[:alnum:]]*[\\t ]*=[\\t ]*(\\\\(.*\\\\)|[$_[:alpha:]][$_[:alnum:]]*)[\\t ]*=>.*)\\n\"\n    ++\t \"^(exports\\\\.[$_[:alpha:]][$_[:alnum:]]*[\\t ]*=[\\t ]*(\\\\(.*\\\\)|[$_[:alpha:]][$_[:alnum:]]*)[\\t ]*=>.*)\\n\"\n     +\t /* match assign function to LHS */\n     +\t \"^(.*=[\\t ]*function[\\t ]*([$_[:alpha:]][$_[:alnum:]]*)?[\\t ]*\\\\(.*)\\n\"\n     +\t /* match normal function in object literal */\n    @@ userdiff.c: PATTERNS(\"java\",\n     +\t /* match function in class */\n     +\t \"^[\\t ]*((static[\\t ]+)?((async|get|set)[\\t ]+)?[$_[:alpha:]][$_[:alnum:]]*[\\t ]*\\\\(.*)\",\n     +\t /* word regex */\n    -+\t /* hexIntegerLiteral and bigHexIntegerLiteral*/\n    -+\t \"0[xX][0-9a-fA-F][_0-9a-fA-F]*n?|\"\n    -+\t /* octalIntegerLiteral and bigOctalIntegerLiteral */\n    -+\t \"0[oO]?[0-7][_0-7]*n?|\"\n    -+\t /* binaryIntegerLiteral and bigBinaryIntegerLiteral */\n    -+\t \"(0[bB][01][_01]*n?)|\"\n    -+\t /* decimalLiteral */\n    -+\t \"(0|[1-9][_0-9]*)?\\\\.?[0-9][_0-9]*([eE][+-]?[_0-9]+)?|\"\n    -+\t /* bigDecimalLiteral */\n    -+\t \"(0|[1-9][_0-9]*)n|\"\n    ++\t /* hexIntegerLiteral, octalIntegerLiteral, binaryIntegerLiteral, DecimalLiteral and its big version */\n    ++\t \"(0[xXoObB])?[0-9a-fA-F][_0-9a-fA-F]*n?\"\n    ++\t /* DecimalLiteral may be float */\n    ++\t \"|(0|[1-9][_0-9]*)?\\\\.?[0-9][_0-9]*([eE][+-]?[_0-9]+)?\"\n     +\t /* punctuations */\n    -+\t \"\\\\{|\\\\}|\\\\(|\\\\)|\\\\.|\\\\.{3}|;|,|<|>|<=|>=|==|!=|={3}|!==|\\\\+|-|\\\\*|/|%|\\\\*{2}|\"\n    -+\t \"\\\\+{2}|--|<<|>>|>>>|&|\\\\||\\\\^|!|~|&&|\\\\|{2}|\\\\?{1,2}|:|=|\\\\+=|-=|\\\\*=|%=|\\\\*{2}=|\"\n    -+\t \"<<=|>>=|>>>=|&=|\\\\|=|\\\\^=|&&=|\\\\|{2}=|\\\\?{2}=|=>|\"\n    ++\t \"|\\\\.{3}|<=|>=|==|!=|={3}|!==|\\\\*{2}|\\\\+{2}|--|<<|>>\"\n    ++\t \"|>>>|&&|\\\\|{2}|\\\\?{2}|\\\\+=|-=|\\\\*=|%=|\\\\*{2}=\"\n    ++\t \"|<<=|>>=|>>>=|&=|\\\\|=|\\\\^=|&&=|\\\\|{2}=|\\\\?{2}=|=>\"\n     +\t /* identifiers */\n    -+\t \"[$_[:alpha:]][$_[:alnum:]]*\"),\n    ++\t \"|[$_[:alpha:]][$_[:alnum:]]*\"),\n      PATTERNS(\"markdown\",\n      \t \"^ {0,3}#{1,6}[ \\t].*\",\n      \t /* -- */\n\n Documentation/gitattributes.txt               |  2 +\n ...avascript-assignment-of-anonymous-function |  4 ++\n .../javascript-assignment-of-arrow-function   |  4 ++\n .../javascript-assignment-of-arrow-function-2 |  4 ++\n .../javascript-assignment-of-arrow-function-3 |  4 ++\n .../javascript-assignment-of-named-function   |  4 ++\n t/t4018/javascript-async-function             |  4 ++\n t/t4018/javascript-export-async-function      |  4 ++\n t/t4018/javascript-export-function            |  4 ++\n t/t4018/javascript-exports-anomyous-function  |  4 ++\n .../javascript-exports-anomyous-function-2    |  4 ++\n t/t4018/javascript-exports-function           |  4 ++\n t/t4018/javascript-function                   |  4 ++\n t/t4018/javascript-function-2                 | 10 ++++\n t/t4018/javascript-function-belong-to-IIFE    |  6 +++\n t/t4018/javascript-function-in-class          |  6 +++\n t/t4018/javascript-function-in-class-2        | 11 ++++\n t/t4018/javascript-function-in-class-3        | 10 ++++\n t/t4018/javascript-function-in-object-literal |  7 +++\n t/t4018/javascript-generator-function         |  4 ++\n t/t4018/javascript-generator-function-2       |  4 ++\n t/t4018/javascript-getter-function-in-class   |  6 +++\n t/t4018/javascript-setter-function-in-class   |  6 +++\n .../javascript-skip-function-call-statement   |  7 +++\n t/t4018/javascript-skip-keywords              | 34 ++++++++++++\n t/t4018/javascript-static-function-in-class   |  6 +++\n t/t4034-diff-words.sh                         |  1 +\n t/t4034/javascript/expect                     | 54 +++++++++++++++++++\n t/t4034/javascript/post                       | 33 ++++++++++++\n t/t4034/javascript/pre                        | 33 ++++++++++++\n userdiff.c                                    | 32 +++++++++++\n 31 files changed, 320 insertions(+)\n create mode 100644 t/t4018/javascript-assignment-of-anonymous-function\n create mode 100644 t/t4018/javascript-assignment-of-arrow-function\n create mode 100644 t/t4018/javascript-assignment-of-arrow-function-2\n create mode 100644 t/t4018/javascript-assignment-of-arrow-function-3\n create mode 100644 t/t4018/javascript-assignment-of-named-function\n create mode 100644 t/t4018/javascript-async-function\n create mode 100644 t/t4018/javascript-export-async-function\n create mode 100644 t/t4018/javascript-export-function\n create mode 100644 t/t4018/javascript-exports-anomyous-function\n create mode 100644 t/t4018/javascript-exports-anomyous-function-2\n create mode 100644 t/t4018/javascript-exports-function\n create mode 100644 t/t4018/javascript-function\n create mode 100644 t/t4018/javascript-function-2\n create mode 100644 t/t4018/javascript-function-belong-to-IIFE\n create mode 100644 t/t4018/javascript-function-in-class\n create mode 100644 t/t4018/javascript-function-in-class-2\n create mode 100644 t/t4018/javascript-function-in-class-3\n create mode 100644 t/t4018/javascript-function-in-object-literal\n create mode 100644 t/t4018/javascript-generator-function\n create mode 100644 t/t4018/javascript-generator-function-2\n create mode 100644 t/t4018/javascript-getter-function-in-class\n create mode 100644 t/t4018/javascript-setter-function-in-class\n create mode 100644 t/t4018/javascript-skip-function-call-statement\n create mode 100644 t/t4018/javascript-skip-keywords\n create mode 100644 t/t4018/javascript-static-function-in-class\n create mode 100644 t/t4034/javascript/expect\n create mode 100644 t/t4034/javascript/post\n create mode 100644 t/t4034/javascript/pre\n\ndiff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\nindex 60984a4682..a8e3e4d735 100644\n--- a/Documentation/gitattributes.txt\n+++ b/Documentation/gitattributes.txt\n@@ -828,6 +828,8 @@ patterns are available:\n \n - `java` suitable for source code in the Java language.\n \n+- `javascript` suitable for source code in the JavaScript language.\n+\n - `markdown` suitable for Markdown documents.\n \n - `matlab` suitable for source code in the MATLAB and Octave languages.\ndiff --git a/t/t4018/javascript-assignment-of-anonymous-function b/t/t4018/javascript-assignment-of-anonymous-function\nnew file mode 100644\nindex 0000000000..b6f2ccccfc\n--- /dev/null\n+++ b/t/t4018/javascript-assignment-of-anonymous-function\n@@ -0,0 +1,4 @@\n+const RIGHT = function (a, b) {\n+\t\n+    return a + b; // ChangeMe\n+};\ndiff --git a/t/t4018/javascript-assignment-of-arrow-function b/t/t4018/javascript-assignment-of-arrow-function\nnew file mode 100644\nindex 0000000000..24ce517b7a\n--- /dev/null\n+++ b/t/t4018/javascript-assignment-of-arrow-function\n@@ -0,0 +1,4 @@\n+const RIGHT = (a, b) => {\n+\t\n+    return a + b; // ChangeMe\n+};\ndiff --git a/t/t4018/javascript-assignment-of-arrow-function-2 b/t/t4018/javascript-assignment-of-arrow-function-2\nnew file mode 100644\nindex 0000000000..bbf5de369e\n--- /dev/null\n+++ b/t/t4018/javascript-assignment-of-arrow-function-2\n@@ -0,0 +1,4 @@\n+const RIGHT = (a, b)=>{\n+\t\n+    return a + b; // ChangeMe\n+};\ndiff --git a/t/t4018/javascript-assignment-of-arrow-function-3 b/t/t4018/javascript-assignment-of-arrow-function-3\nnew file mode 100644\nindex 0000000000..4a07aa3259\n--- /dev/null\n+++ b/t/t4018/javascript-assignment-of-arrow-function-3\n@@ -0,0 +1,4 @@\n+const RIGHT=test=>{\n+\t\n+    return test + 1; // ChangeMe\n+};\ndiff --git a/t/t4018/javascript-assignment-of-named-function b/t/t4018/javascript-assignment-of-named-function\nnew file mode 100644\nindex 0000000000..bfc486ebef\n--- /dev/null\n+++ b/t/t4018/javascript-assignment-of-named-function\n@@ -0,0 +1,4 @@\n+const RIGHT = function test (a, b) {\n+\t\n+    return a + b; // ChangeMe\n+};\ndiff --git a/t/t4018/javascript-async-function b/t/t4018/javascript-async-function\nnew file mode 100644\nindex 0000000000..993e6926bf\n--- /dev/null\n+++ b/t/t4018/javascript-async-function\n@@ -0,0 +1,4 @@\n+async function RIGHT(a, b) {\n+  \n+  return a + b; // ChangeMe\n+}\ndiff --git a/t/t4018/javascript-export-async-function b/t/t4018/javascript-export-async-function\nnew file mode 100644\nindex 0000000000..fecbd669d7\n--- /dev/null\n+++ b/t/t4018/javascript-export-async-function\n@@ -0,0 +1,4 @@\n+export async function RIGHT(a, b) {\n+  \n+  return a + b; // ChangeMe\n+}\ndiff --git a/t/t4018/javascript-export-function b/t/t4018/javascript-export-function\nnew file mode 100644\nindex 0000000000..b5acbb2b08\n--- /dev/null\n+++ b/t/t4018/javascript-export-function\n@@ -0,0 +1,4 @@\n+export function RIGHT(a, b) {\n+  \n+  return a + b; // ChangeMe\n+}\ndiff --git a/t/t4018/javascript-exports-anomyous-function b/t/t4018/javascript-exports-anomyous-function\nnew file mode 100644\nindex 0000000000..6786cbda8d\n--- /dev/null\n+++ b/t/t4018/javascript-exports-anomyous-function\n@@ -0,0 +1,4 @@\n+exports.setFlagged = RIGHT => {\n+\t\n+    return ChangeMe;\n+};\ndiff --git a/t/t4018/javascript-exports-anomyous-function-2 b/t/t4018/javascript-exports-anomyous-function-2\nnew file mode 100644\nindex 0000000000..883569f40d\n--- /dev/null\n+++ b/t/t4018/javascript-exports-anomyous-function-2\n@@ -0,0 +1,4 @@\n+exports.RIGHT = (a, b, runtime) => {\n+\t\n+    return ChangeMe;\n+};\ndiff --git a/t/t4018/javascript-exports-function b/t/t4018/javascript-exports-function\nnew file mode 100644\nindex 0000000000..63b79f5991\n--- /dev/null\n+++ b/t/t4018/javascript-exports-function\n@@ -0,0 +1,4 @@\n+exports.RIGHT = function(document) {\n+    \n+    return ChangeMe;\n+}\ndiff --git a/t/t4018/javascript-function b/t/t4018/javascript-function\nnew file mode 100644\nindex 0000000000..0cc0bf54e7\n--- /dev/null\n+++ b/t/t4018/javascript-function\n@@ -0,0 +1,4 @@\n+function RIGHT(a, b) {\n+\n+  return a + b; // ChangeMe\n+}\ndiff --git a/t/t4018/javascript-function-2 b/t/t4018/javascript-function-2\nnew file mode 100644\nindex 0000000000..06cfb779f0\n--- /dev/null\n+++ b/t/t4018/javascript-function-2\n@@ -0,0 +1,10 @@\n+function test(a, b) {\n+  return {\n+\t\t\tRIGHT: function () {\n+\t\t\t\tcurrentUpdateRemovedChunks.forEach(function (chunkId) {\n+\t\t\t\t\tdelete $installedChunks$[chunkId];\n+\t\t\t\t});\n+\t\t\t\tcurrentUpdateRemovedChunks = ChangeMe;\n+   }\n+  }\n+}\ndiff --git a/t/t4018/javascript-function-belong-to-IIFE b/t/t4018/javascript-function-belong-to-IIFE\nnew file mode 100644\nindex 0000000000..6e5fe858c0\n--- /dev/null\n+++ b/t/t4018/javascript-function-belong-to-IIFE\n@@ -0,0 +1,6 @@\n+(function () {\n+  this.$RIGHT = function (needle, modifier) {\n+      let a = 5;\n+      return ChangeMe;\n+  };\n+}).call(aaaa.prototype);\ndiff --git a/t/t4018/javascript-function-in-class b/t/t4018/javascript-function-in-class\nnew file mode 100644\nindex 0000000000..0cc0a26612\n--- /dev/null\n+++ b/t/t4018/javascript-function-in-class\n@@ -0,0 +1,6 @@\n+class Test {\n+  RIGHT() {\n+    let a = 4;\n+    let b = ChangeMe;\n+  }\n+}\ndiff --git a/t/t4018/javascript-function-in-class-2 b/t/t4018/javascript-function-in-class-2\nnew file mode 100644\nindex 0000000000..725495fe55\n--- /dev/null\n+++ b/t/t4018/javascript-function-in-class-2\n@@ -0,0 +1,11 @@\n+class Test {\n+  RIGHT(\n+      aaaaaaaaaa,\n+      bbbbbbbbbb,\n+      cccccccccc,\n+      dddddddddd\n+  ) {\n+    let a = 4;\n+    let b = ChangeMe;\n+  }\n+}\ndiff --git a/t/t4018/javascript-function-in-class-3 b/t/t4018/javascript-function-in-class-3\nnew file mode 100644\nindex 0000000000..e9b20728b2\n--- /dev/null\n+++ b/t/t4018/javascript-function-in-class-3\n@@ -0,0 +1,10 @@\n+class Test {\n+  RIGHT(aaaaaaaaaa,\n+      bbbbbbbbbb,\n+      cccccccccc,\n+      dddddddddd\n+  ) {\n+    let a = 4;\n+    let b = ChangeMe;\n+  }\n+}\ndiff --git a/t/t4018/javascript-function-in-object-literal b/t/t4018/javascript-function-in-object-literal\nnew file mode 100644\nindex 0000000000..021cc706dd\n--- /dev/null\n+++ b/t/t4018/javascript-function-in-object-literal\n@@ -0,0 +1,7 @@\n+const obj = {\n+    RIGHT: function (elems, callback, arg) {\n+        var length, value;\n+        // ...\n+        return ChangeMe\n+    }\n+}\ndiff --git a/t/t4018/javascript-generator-function b/t/t4018/javascript-generator-function\nnew file mode 100644\nindex 0000000000..dc7793939f\n--- /dev/null\n+++ b/t/t4018/javascript-generator-function\n@@ -0,0 +1,4 @@\n+function* RIGHT(a, b) {\n+  \n+  return a + b; // ChangeMe\n+}\ndiff --git a/t/t4018/javascript-generator-function-2 b/t/t4018/javascript-generator-function-2\nnew file mode 100644\nindex 0000000000..950676a612\n--- /dev/null\n+++ b/t/t4018/javascript-generator-function-2\n@@ -0,0 +1,4 @@\n+function *RIGHT(a, b) {\n+  \n+  return a + b; // ChangeMe\n+}\ndiff --git a/t/t4018/javascript-getter-function-in-class b/t/t4018/javascript-getter-function-in-class\nnew file mode 100644\nindex 0000000000..9a5aee39f7\n--- /dev/null\n+++ b/t/t4018/javascript-getter-function-in-class\n@@ -0,0 +1,6 @@\n+class Test {\n+  get RIGHT() {\n+    let a = 4;\n+    let b = ChangeMe;\n+  }\n+}\ndiff --git a/t/t4018/javascript-setter-function-in-class b/t/t4018/javascript-setter-function-in-class\nnew file mode 100644\nindex 0000000000..dc5f288665\n--- /dev/null\n+++ b/t/t4018/javascript-setter-function-in-class\n@@ -0,0 +1,6 @@\n+class Test {\n+  set RIGHT() {\n+    let a = 4;\n+    let b = ChangeMe;\n+  }\n+}\ndiff --git a/t/t4018/javascript-skip-function-call-statement b/t/t4018/javascript-skip-function-call-statement\nnew file mode 100644\nindex 0000000000..321993c27e\n--- /dev/null\n+++ b/t/t4018/javascript-skip-function-call-statement\n@@ -0,0 +1,7 @@\n+class Test {\n+  static RIGHT() {\n+    haha();\n+    haha2()\n+    let b = ChangeMe;\n+  }\n+}\ndiff --git a/t/t4018/javascript-skip-keywords b/t/t4018/javascript-skip-keywords\nnew file mode 100644\nindex 0000000000..5584970b58\n--- /dev/null\n+++ b/t/t4018/javascript-skip-keywords\n@@ -0,0 +1,34 @@\n+function RIGHT(a, b) {\n+  import(\"./async1\")\n+  if (a > 1) {\n+    // ...\n+  }\n+  do {\n+    // ...\n+  } while (i < 5);\n+  for (const element of array1) {\n+    console.log(element)\n+  }\n+  with(o) {\n+    console.log(x)\n+  }\n+  switch (expr) {\n+    case 'a':\n+      // ...\n+      break;\n+    case 'b':\n+      // ...\n+      break;\n+    default:\n+      // ...\n+  }\n+  try {\n+    // ...\n+    return (a + c)\n+  } \n+  catch (error) {\n+    // ...\n+  }\n+\n+  return a + b; // ChangeMe\n+}\ndiff --git a/t/t4018/javascript-static-function-in-class b/t/t4018/javascript-static-function-in-class\nnew file mode 100644\nindex 0000000000..fbf0b7ca3d\n--- /dev/null\n+++ b/t/t4018/javascript-static-function-in-class\n@@ -0,0 +1,6 @@\n+class Test {\n+  static RIGHT() {\n+    let a = 4;\n+    let b = ChangeMe;\n+  }\n+}\ndiff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh\nindex d5abcf4b4c..33073edeca 100755\n--- a/t/t4034-diff-words.sh\n+++ b/t/t4034-diff-words.sh\n@@ -324,6 +324,7 @@ test_language_driver dts\n test_language_driver fortran\n test_language_driver html\n test_language_driver java\n+test_language_driver javascript\n test_language_driver matlab\n test_language_driver objc\n test_language_driver pascal\ndiff --git a/t/t4034/javascript/expect b/t/t4034/javascript/expect\nnew file mode 100644\nindex 0000000000..419d61903b\n--- /dev/null\n+++ b/t/t4034/javascript/expect\n@@ -0,0 +1,54 @@\n+<BOLD>diff --git a/pre b/post<RESET>\n+<BOLD>index 18f4796..46f9b62 100644<RESET>\n+<BOLD>--- a/pre<RESET>\n+<BOLD>+++ b/post<RESET>\n+<CYAN>@@ -1,33 +1,33 @@<RESET>\n+// DecimalLiteral<RESET>\n+<RED>123<RESET>\n+<RED>0.123<RESET>\n+<RED>.123<RESET>\n+<RED>0.123e+5<RESET>\n+<RED>0.123E+5<RESET>\n+<RED>0.123e5<RESET>\n+<RED>1222222222222222223334444n<RESET><GREEN>124<RESET>\n+<GREEN>0.124<RESET>\n+<GREEN>.124<RESET>\n+<GREEN>0.123e-5<RESET>\n+<GREEN>0.123E-5<RESET>\n+<GREEN>0.123E5<RESET>\n+<GREEN>12222222222222222233344445n<RESET>\n+// HexIntegerLiteral<RESET>\n+<RED>0x10<RESET>\n+<RED>0X6Fa1<RESET>\n+<RED>0x123_456<RESET>\n+<RED>0x1234182989812f1289an<RESET><GREEN>0x11<RESET>\n+<GREEN>0X5Fa1<RESET>\n+<GREEN>0x123_756<RESET>\n+<GREEN>0x1234182989812f1289bn<RESET>\n+// OctalIntegerLiteral<RESET>\n+<RED>05<RESET>\n+<RED>0o6<RESET>\n+<RED>0O7<RESET>\n+<RED>0512_567<RESET>\n+<RED>0o424242424242424242424242424242666666n<RESET><GREEN>06<RESET>\n+<GREEN>0o5<RESET>\n+<GREEN>0O4<RESET>\n+<GREEN>0511_567<RESET>\n+<GREEN>0o424242424242424242424242424242666667n<RESET>\n+// BinaryIntegerLiteral<RESET>\n+<RED>0b1001<RESET>\n+<RED>0B0110<RESET>\n+<RED>0b0001_1001_0011<RESET>\n+<RED>0b1111111111111111111111111111111111111n<RESET><GREEN>0b1101<RESET>\n+<GREEN>0B0010<RESET>\n+<GREEN>0b0001_1101_0011<RESET>\n+<GREEN>0b11111111111111000011111111111111111n<RESET>\n+// punctuations<RESET>\n+{<RED>a<RESET><GREEN>b<RESET>} (<RED>a<RESET><GREEN>b<RESET>)\n+<RED>a<RESET><GREEN>b<RESET>;\n+[<RED>1,<RESET>2<GREEN>,3<RESET>]\n+[<RED>1, 2,<RESET> ...<RED>params<RESET><GREEN>params_v2<RESET> ]\n+a<RED><=<RESET><GREEN>=<RESET>2 a<RED>>=<RESET><GREEN>=<RESET>2 a<RED>==<RESET><GREEN>=<RESET>2 a<RED>!=<RESET><GREEN>=<RESET>2 a<RED>===<RESET><GREEN>=<RESET>2 a<RED>!==<RESET><GREEN>=<RESET>2 a<RED>^=<RESET><GREEN>=<RESET>2 a<RED>=><RESET><GREEN>=<RESET>2\n+a<RED>+=<RESET><GREEN>-=<RESET>b a<RED>*=<RESET><GREEN>%=<RESET>b a<RED>**=<RESET><GREEN>&&=<RESET>b a<RED>||=<RESET><GREEN>|=<RESET>b\n+b<RED>+<RESET><GREEN>-<RESET>c a<RED>--<RESET><GREEN>++<RESET> a<RED>>><RESET><GREEN><<<RESET>b a<RED>>>><RESET><GREEN>>>>=<RESET>b a<RED>>>=<RESET><GREEN><<=<RESET>b\n+a<RED>&&<RESET><GREEN>&<RESET>b a<RED>||<RESET><GREEN>|<RESET>b a<RED>&&=<RESET><GREEN>??=<RESET>b\ndiff --git a/t/t4034/javascript/post b/t/t4034/javascript/post\nnew file mode 100644\nindex 0000000000..46f9b627e4\n--- /dev/null\n+++ b/t/t4034/javascript/post\n@@ -0,0 +1,33 @@\n+// DecimalLiteral\n+124\n+0.124\n+.124\n+0.123e-5\n+0.123E-5\n+0.123E5\n+12222222222222222233344445n\n+// HexIntegerLiteral\n+0x11\n+0X5Fa1\n+0x123_756\n+0x1234182989812f1289bn\n+// OctalIntegerLiteral\n+06\n+0o5\n+0O4\n+0511_567\n+0o424242424242424242424242424242666667n\n+// BinaryIntegerLiteral\n+0b1101\n+0B0010\n+0b0001_1101_0011\n+0b11111111111111000011111111111111111n\n+// punctuations\n+{b} (b)\n+b;\n+[2,3]\n+[ ...params_v2 ]\n+a=2 a=2 a=2 a=2 a=2 a=2 a=2 a=2\n+a-=b a%=b a&&=b a|=b\n+b-c a++ a<<b a>>>=b a<<=b\n+a&b a|b a??=b\ndiff --git a/t/t4034/javascript/pre b/t/t4034/javascript/pre\nnew file mode 100644\nindex 0000000000..18f479688c\n--- /dev/null\n+++ b/t/t4034/javascript/pre\n@@ -0,0 +1,33 @@\n+// DecimalLiteral\n+123\n+0.123\n+.123\n+0.123e+5\n+0.123E+5\n+0.123e5\n+1222222222222222223334444n\n+// HexIntegerLiteral\n+0x10\n+0X6Fa1\n+0x123_456\n+0x1234182989812f1289an\n+// OctalIntegerLiteral\n+05\n+0o6\n+0O7\n+0512_567\n+0o424242424242424242424242424242666666n\n+// BinaryIntegerLiteral\n+0b1001\n+0B0110\n+0b0001_1001_0011\n+0b1111111111111111111111111111111111111n\n+// punctuations\n+{a} (a)\n+a;\n+[1,2]\n+[ 1, 2, ...params ]\n+a<=2 a>=2 a==2 a!=2 a===2 a!==2 a^=2 a=>2\n+a+=b a*=b a**=b a||=b\n+b+c a-- a>>b a>>>b a>>=b\n+a&&b a||b a&&=b\ndiff --git a/userdiff.c b/userdiff.c\nindex 8578cb0d12..51bfe4021d 100644\n--- a/userdiff.c\n+++ b/userdiff.c\n@@ -168,6 +168,38 @@ PATTERNS(\"java\",\n \t \"|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?\"\n \t \"|[-+*/<>%&^|=!]=\"\n \t \"|--|\\\\+\\\\+|<<=?|>>>?=?|&&|\\\\|\\\\|\"),\n+\n+PATTERNS(\"javascript\",\n+\t /* don't match the expression may contain parenthesis, because it is not a function declaration */\n+\t \"!^[ \\t]*(if|do|while|for|with|switch|catch|import|return)\\n\"\n+\t /* don't match statement */\n+\t \"!;\\n\"\n+\t /* match normal function */\n+\t \"^((export[\\t ]+)?(async[\\t ]+)?function[\\t ]*[\\t *]*[$_[:alpha:]][$_[:alnum:]]*[\\t ]*\\\\(.*)\\n\"\n+\t /* match JavaScript variable declaration with a lambda expression */\n+\t \"^[\\t ]*((const|let|var)[\\t ]*[$_[:alpha:]][$_[:alnum:]]*[\\t ]*=[\\t ]*\"\n+\t \"(\\\\(.*\\\\)|[$_[:alpha:]][$_[:alnum:]]*)[\\t ]*=>[\\t ]*\\\\{?)\\n\"\n+\t /* match exports for anonymous fucntion */\n+\t \"^(exports\\\\.[$_[:alpha:]][$_[:alnum:]]*[\\t ]*=[\\t ]*(\\\\(.*\\\\)|[$_[:alpha:]][$_[:alnum:]]*)[\\t ]*=>.*)\\n\"\n+\t /* match assign function to LHS */\n+\t \"^(.*=[\\t ]*function[\\t ]*([$_[:alpha:]][$_[:alnum:]]*)?[\\t ]*\\\\(.*)\\n\"\n+\t /* match normal function in object literal */\n+\t \"^[\\t ]*([$_[:alpha:]][$_[:alnum:]]*[\\t ]*:[\\t ]*function[\\t ].*)\\n\"\n+\t /* don't match the function in class, which has more than one ident level */\n+\t \"!^(\\t{2,}|[ ]{5,})\\n\"\n+\t /* match function in class */\n+\t \"^[\\t ]*((static[\\t ]+)?((async|get|set)[\\t ]+)?[$_[:alpha:]][$_[:alnum:]]*[\\t ]*\\\\(.*)\",\n+\t /* word regex */\n+\t /* hexIntegerLiteral, octalIntegerLiteral, binaryIntegerLiteral, DecimalLiteral and its big version */\n+\t \"(0[xXoObB])?[0-9a-fA-F][_0-9a-fA-F]*n?\"\n+\t /* DecimalLiteral may be float */\n+\t \"|(0|[1-9][_0-9]*)?\\\\.?[0-9][_0-9]*([eE][+-]?[_0-9]+)?\"\n+\t /* punctuations */\n+\t \"|\\\\.{3}|<=|>=|==|!=|={3}|!==|\\\\*{2}|\\\\+{2}|--|<<|>>\"\n+\t \"|>>>|&&|\\\\|{2}|\\\\?{2}|\\\\+=|-=|\\\\*=|%=|\\\\*{2}=\"\n+\t \"|<<=|>>=|>>>=|&=|\\\\|=|\\\\^=|&&=|\\\\|{2}=|\\\\?{2}=|=>\"\n+\t /* identifiers */\n+\t \"|[$_[:alpha:]][$_[:alnum:]]*\"),\n PATTERNS(\"markdown\",\n \t \"^ {0,3}#{1,6}[ \\t].*\",\n \t /* -- */\n-- \n2.35.1.273.ge6ebfd0e8c.dirty\n\n"},{"id":"451222","messageId":"CAN_FRN+Q6L-RhUNrpoyNzL-dmCNYKB476Wo9AKQst-4oiS-T8Q@mail.gmail.com","threadId":"57508","inReplyTo":"504f9f0d-a1ab-5ce7-d744-9398b0e7e68a@kdbg.org","subject":"Re: [GSoC][PATCH 1/1] Add a diff driver for JavaScript languages.","fromName":"xing zhi jiang","fromEmail":"a97410985new@gmail.com","sentAt":"2022-03-12T16:59:21Z","receivedAt":"2022-03-12T16:59:37Z","isPatch":true,"sender":{"key":"a97410985new@gmail.com","avatar":"https://gravatar.com/avatar/a66532d6438c89cb1989916e4a054b2c0c069296379adef42f9fbfca6afa6b06?d=mp&s=160"},"body":"On Tue, 8 Mar 2022 at 14:47, Johannes Sixt <j6t@kdbg.org> wrote:\n> I meant a multi-line function call like so, where the first argument is\n> on the same line with the function name:\n>\n>  RIGHT(aaaaaaaaaa,\n>      bbbbbbbbbb,\n>     cccccccccc,\n>    dddddddddd\n> ) {\n> ...\n\nOK, I got it. I thought it is not a difficult case, but I add a new\ntest case for this.\n\n>  <No, I refer to the whitespace around \"=>\" that is not required here, but\n>  was required in the previous expression.\n\n I think the space is required, ex:\nhttps://github.com/webpack/webpack/blob/c181294865dca01b28e6e316636fef5f2aad4eb6/lib/dependencies/DynamicExports.js#L17\n. The link above it has space before 「=>」.\n\nBest regards.\n"},{"id":"451270","messageId":"21ca0635-8a70-14ff-6dbc-213eeba85750@kdbg.org","threadId":"57508","inReplyTo":"20220312164803.57909-1-a97410985new@gmail.com","subject":"Re: [GSoC][PATCH v2] Add a diff driver for JavaScript languages.","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2022-03-13T21:54:57Z","receivedAt":"2022-03-13T21:55:03Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"When you send a new iteration of a patch or patch set, it is customary\non this list to include everyone who took part in the earlier rounds in\nthe Cc: list.\n\nAm 12.03.22 um 17:48 schrieb xing zhi jiang:\n> In the xfunction part that matches normal functions,\n> a variable declaration with an assignment of function, the function declaration\n> in the class, and also the function is object literal's property[1].\n> \n> And in the word regex part, that matches numbers, punctuations, and also the\n> JavaScript identifier.\n> This part reference the formal ECMA specification[2].\n> \n> [1]https://github.com/jquery/jquery/blob/de5398a6ad088dc006b46c6a870a2a053f4cd663/src/core.js#L201\n> [2]https://262.ecma-international.org/12.0/#sec-ecmascript-language-lexical-grammar\n> \n> Signed-off-by: xing zhi jiang <a97410985new@gmail.com>\n> ---\n\n> diff --git a/userdiff.c b/userdiff.c\n> index 8578cb0d12..51bfe4021d 100644\n> --- a/userdiff.c\n> +++ b/userdiff.c\n> @@ -168,6 +168,38 @@ PATTERNS(\"java\",\n>  \t \"|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?\"\n>  \t \"|[-+*/<>%&^|=!]=\"\n>  \t \"|--|\\\\+\\\\+|<<=?|>>>?=?|&&|\\\\|\\\\|\"),\n> +\n> +PATTERNS(\"javascript\",\n> +\t /* don't match the expression may contain parenthesis, because it is not a function declaration */\n> +\t \"!^[ \\t]*(if|do|while|for|with|switch|catch|import|return)\\n\"\n> +\t /* don't match statement */\n> +\t \"!;\\n\"\n> +\t /* match normal function */\n> +\t \"^((export[\\t ]+)?(async[\\t ]+)?function[\\t ]*[\\t *]*[$_[:alpha:]][$_[:alnum:]]*[\\t ]*\\\\(.*)\\n\"\n> +\t /* match JavaScript variable declaration with a lambda expression */\n> +\t \"^[\\t ]*((const|let|var)[\\t ]*[$_[:alpha:]][$_[:alnum:]]*[\\t ]*=[\\t ]*\"\n> +\t \"(\\\\(.*\\\\)|[$_[:alpha:]][$_[:alnum:]]*)[\\t ]*=>[\\t ]*\\\\{?)\\n\"\n\nIt would help readability if this second line of this regex were\nindented because it is a continuation of the first line.\n\n> +\t /* match exports for anonymous fucntion */\n> +\t \"^(exports\\\\.[$_[:alpha:]][$_[:alnum:]]*[\\t ]*=[\\t ]*(\\\\(.*\\\\)|[$_[:alpha:]][$_[:alnum:]]*)[\\t ]*=>.*)\\n\"\n> +\t /* match assign function to LHS */\n> +\t \"^(.*=[\\t ]*function[\\t ]*([$_[:alpha:]][$_[:alnum:]]*)?[\\t ]*\\\\(.*)\\n\"\n\nThis should be written as\n\n\t \"^(.*=[\\t ]*function[\\t ]*([$_[:alpha:]][$_[:alnum:]]*[\\t ]*)?\\\\(.*)\\n\"\n\nNotice that the whitespace after the identifier can only appear when\nthere is actually an identifier. The point is to reduce the different\nmatches permitted by the sub-expression \"[\\t ]*[\\t ]*\" when there is no\nidentifier in the text.\n\nCan the keyword function ever be followed by a number? I guess not. Then\n[$_[:alpha:]][$_[:alnum:]]* could be reduced to [$_[:alnum:]]+\n\n> +\t /* match normal function in object literal */\n> +\t \"^[\\t ]*([$_[:alpha:]][$_[:alnum:]]*[\\t ]*:[\\t ]*function[\\t ].*)\\n\"\n> +\t /* don't match the function in class, which has more than one ident level */\n> +\t \"!^(\\t{2,}|[ ]{5,})\\n\"\n> +\t /* match function in class */\n> +\t \"^[\\t ]*((static[\\t ]+)?((async|get|set)[\\t ]+)?[$_[:alpha:]][$_[:alnum:]]*[\\t ]*\\\\(.*)\",> +\t /* word regex */\n> +\t /* hexIntegerLiteral, octalIntegerLiteral, binaryIntegerLiteral, DecimalLiteral and its big version */\n> +\t \"(0[xXoObB])?[0-9a-fA-F][_0-9a-fA-F]*n?\"\n> +\t /* DecimalLiteral may be float */\n> +\t \"|(0|[1-9][_0-9]*)?\\\\.?[0-9][_0-9]*([eE][+-]?[_0-9]+)?\"\n\nHaving alternatives that begin with an optional part make the regex\nevaluation comparatively inefficient. In particular, both alternatives\nabove match a decimal integer. I suggest to have the first alternative\nonly for hex, octal, and binary integers, and the second for all decimal\nnumbers including floatingpoint:\n\n\t /* hexIntegerLiteral, octalIntegerLiteral, binaryIntegerLiteral, and\ntheir big versions */\n\t \"0[xXoObB][_0-9a-fA-F]+n?\"\n\t /* DecimalLiteral may be float */\n\t \"|[0-9][_0-9]*(\\\\.[_0-9]*|n)?([eE][+-]?[_0-9]+)?\"\n\nand if floating point literals can begin with a decimal point, then we\nalso need\n\n\t \"|\\\\.[0-9][_0-9]*([eE][+-]?[_0-9]+)?\"\n\n> +\t /* punctuations */\n> +\t \"|\\\\.{3}|<=|>=|==|!=|={3}|!==|\\\\*{2}|\\\\+{2}|--|<<|>>\"\n> +\t \"|>>>|&&|\\\\|{2}|\\\\?{2}|\\\\+=|-=|\\\\*=|%=|\\\\*{2}=\"\n> +\t \"|<<=|>>=|>>>=|&=|\\\\|=|\\\\^=|&&=|\\\\|{2}=|\\\\?{2}=|=>\"\n> +\t /* identifiers */\n> +\t \"|[$_[:alpha:]][$_[:alnum:]]*\"),\n>  PATTERNS(\"markdown\",\n>  \t \"^ {0,3}#{1,6}[ \\t].*\",\n>  \t /* -- */\n\n-- Hannes\n"},{"id":"451331","messageId":"kl6lwngwqwm2.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"57508","inReplyTo":"20220312164803.57909-1-a97410985new@gmail.com","subject":"Re: [GSoC][PATCH v2] Add a diff driver for JavaScript languages.","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2022-03-14T17:20:37Z","receivedAt":"2022-03-14T17:20:42Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"\nWelcome to the Git project :) I never realized that we didn't include a\nbuilt in JS diff driver, so this would be quite welcome.\n\nI'm not entirely familiar with this part of the system or our regex\nstyle, so I won't comment on those. I'll only comment on the patterns we\nare trying to detect and whether or not we want them.\n\nI'm not 100% clear on the style around diff drivers e.g. how do we\ndecide that we want a pattern or not? I'd appreciate any pointers to\ndocs or commits.\n\nxing zhi jiang <a97410985new@gmail.com> writes:\n\n> diff --git a/t/t4018/javascript-assignment-of-anonymous-function b/t/t4018/javascript-assignment-of-anonymous-function\n> new file mode 100644\n> index 0000000000..b6f2ccccfc\n> --- /dev/null\n> +++ b/t/t4018/javascript-assignment-of-anonymous-function\n> @@ -0,0 +1,4 @@\n> +const RIGHT = function (a, b) {\n> +\t\n> +    return a + b; // ChangeMe\n> +};\n> diff --git a/t/t4018/javascript-assignment-of-arrow-function b/t/t4018/javascript-assignment-of-arrow-function\n> new file mode 100644\n> index 0000000000..24ce517b7a\n> --- /dev/null\n> +++ b/t/t4018/javascript-assignment-of-arrow-function\n> @@ -0,0 +1,4 @@\n> +const RIGHT = (a, b) => {\n> +\t\n> +    return a + b; // ChangeMe\n> +};\n> diff --git a/t/t4018/javascript-assignment-of-arrow-function-2 b/t/t4018/javascript-assignment-of-arrow-function-2\n> new file mode 100644\n> index 0000000000..bbf5de369e\n> --- /dev/null\n> +++ b/t/t4018/javascript-assignment-of-arrow-function-2\n> @@ -0,0 +1,4 @@\n> +const RIGHT = (a, b)=>{\n> +\t\n> +    return a + b; // ChangeMe\n> +};\n> diff --git a/t/t4018/javascript-assignment-of-arrow-function-3 b/t/t4018/javascript-assignment-of-arrow-function-3\n> new file mode 100644\n> index 0000000000..4a07aa3259\n> --- /dev/null\n> +++ b/t/t4018/javascript-assignment-of-arrow-function-3\n> @@ -0,0 +1,4 @@\n> +const RIGHT=test=>{\n> +\t\n> +    return test + 1; // ChangeMe\n> +};\n\nThese are all variable assignments of anonymous functions, so we won't\n'technically' be showing the function name in the diff, but as a\npractical matter, they are _probably_ referred to by this name\nconsistently. So including them makes sense.\n\n> diff --git a/t/t4018/javascript-assignment-of-named-function b/t/t4018/javascript-assignment-of-named-function\n> new file mode 100644\n> index 0000000000..bfc486ebef\n> --- /dev/null\n> +++ b/t/t4018/javascript-assignment-of-named-function\n> @@ -0,0 +1,4 @@\n> +const RIGHT = function test (a, b) {\n> +\t\n> +    return a + b; // ChangeMe\n> +};\n> diff --git a/t/t4018/javascript-async-function b/t/t4018/javascript-async-function\n> new file mode 100644\n> index 0000000000..993e6926bf\n> --- /dev/null\n> +++ b/t/t4018/javascript-async-function\n> @@ -0,0 +1,4 @@\n> +async function RIGHT(a, b) {\n> +  \n> +  return a + b; // ChangeMe\n> +}\n> diff --git a/t/t4018/javascript-export-async-function b/t/t4018/javascript-export-async-function\n> new file mode 100644\n> index 0000000000..fecbd669d7\n> --- /dev/null\n> +++ b/t/t4018/javascript-export-async-function\n> @@ -0,0 +1,4 @@\n> +export async function RIGHT(a, b) {\n> +  \n> +  return a + b; // ChangeMe\n> +}\n> diff --git a/t/t4018/javascript-export-function b/t/t4018/javascript-export-function\n> new file mode 100644\n> index 0000000000..b5acbb2b08\n> --- /dev/null\n> +++ b/t/t4018/javascript-export-function\n> @@ -0,0 +1,4 @@\n> +export function RIGHT(a, b) {\n> +  \n> +  return a + b; // ChangeMe\n> +}\n\nThese look good; the 'export' statements are part of the ES modules\nfeature. I'm not sure if it makes sense to explicitly test these cases\nunless we have reason to believe that the 'export' keyword will affect\nthe matching.\n\n> diff --git a/t/t4018/javascript-exports-anomyous-function b/t/t4018/javascript-exports-anomyous-function\n> new file mode 100644\n> index 0000000000..6786cbda8d\n> --- /dev/null\n> +++ b/t/t4018/javascript-exports-anomyous-function\n> @@ -0,0 +1,4 @@\n> +exports.setFlagged = RIGHT => {\n> +\t\n> +    return ChangeMe;\n> +};\n> diff --git a/t/t4018/javascript-exports-anomyous-function-2 b/t/t4018/javascript-exports-anomyous-function-2\n> new file mode 100644\n> index 0000000000..883569f40d\n> --- /dev/null\n> +++ b/t/t4018/javascript-exports-anomyous-function-2\n> @@ -0,0 +1,4 @@\n> +exports.RIGHT = (a, b, runtime) => {\n> +\t\n> +    return ChangeMe;\n> +};\n> diff --git a/t/t4018/javascript-exports-function b/t/t4018/javascript-exports-function\n> new file mode 100644\n> index 0000000000..63b79f5991\n> --- /dev/null\n> +++ b/t/t4018/javascript-exports-function\n> @@ -0,0 +1,4 @@\n> +exports.RIGHT = function(document) {\n> +    \n> +    return ChangeMe;\n> +}\n\nI don't think we should include 'exports.foo = bar'. To my knowledge,\nthis is _not_ a standard ES feature, but rather the CommonJS module\nsystem popularized by Node.js [1] and other frameworks. To confirm this,\nI searched the ES spec and did not find any reference to exports.* [2].\n\nEven if we wanted to support nonstandard 'language features' (and this\nlabel is tenuous at best, CommonJS is not trying to replace the ES\nmodules standard AFAIK), Node.js is also starting to support ES modules,\nso I don't think this is a good long term direction for Git.\n\n[1] https://nodejs.org/api/modules.html\n[2] https://262.ecma-international.org/12.0/#sec-exports\n\n> diff --git a/t/t4018/javascript-function b/t/t4018/javascript-function\n> new file mode 100644\n> index 0000000000..0cc0bf54e7\n> --- /dev/null\n> +++ b/t/t4018/javascript-function\n> @@ -0,0 +1,4 @@\n> +function RIGHT(a, b) {\n> +\n> +  return a + b; // ChangeMe\n> +}\n> diff --git a/t/t4018/javascript-function-2 b/t/t4018/javascript-function-2\n> new file mode 100644\n> index 0000000000..06cfb779f0\n> --- /dev/null\n> +++ b/t/t4018/javascript-function-2\n> @@ -0,0 +1,10 @@\n> +function test(a, b) {\n> +  return {\n> +\t\t\tRIGHT: function () {\n> +\t\t\t\tcurrentUpdateRemovedChunks.forEach(function (chunkId) {\n> +\t\t\t\t\tdelete $installedChunks$[chunkId];\n> +\t\t\t\t});\n> +\t\t\t\tcurrentUpdateRemovedChunks = ChangeMe;\n> +   }\n> +  }\n> +}\n\nThere is also the ES2015 'method shorthand' syntax [3], e.g. `bar` in:\n\n  const foo = {\n    bar() {\n      console.log('hi');\n    }\n  }\n\n[3] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Method_definitions\n\n> diff --git a/t/t4018/javascript-function-belong-to-IIFE b/t/t4018/javascript-function-belong-to-IIFE\n> new file mode 100644\n> index 0000000000..6e5fe858c0\n> --- /dev/null\n> +++ b/t/t4018/javascript-function-belong-to-IIFE\n> @@ -0,0 +1,6 @@\n> +(function () {\n> +  this.$RIGHT = function (needle, modifier) {\n> +      let a = 5;\n> +      return ChangeMe;\n> +  };\n> +}).call(aaaa.prototype);\n\n Does the IIFE matter in this case? This line:\n\n  this.$RIGHT = function (needle, modifier) {\n\nlooks extremely similar to the previous test of `foo = function bar()`.\n\nOr perhaps this is meant to demonstrate the edge case of \"matching in a\ncomplicated construct\"? If so, perhaps we should test other edge cases\nlike:\n\n   function WRONG() {\n     let RIGHT = function (needle, modifier) {\n         let a = 5;\n         return ChangeMe;\n     };\n   }\n\n> diff --git a/t/t4018/javascript-function-in-class b/t/t4018/javascript-function-in-class\n> new file mode 100644\n> index 0000000000..0cc0a26612\n> --- /dev/null\n> +++ b/t/t4018/javascript-function-in-class\n> @@ -0,0 +1,6 @@\n> +class Test {\n> +  RIGHT() {\n> +    let a = 4;\n> +    let b = ChangeMe;\n> +  }\n> +}\n> diff --git a/t/t4018/javascript-function-in-class-2 b/t/t4018/javascript-function-in-class-2\n> new file mode 100644\n> index 0000000000..725495fe55\n> --- /dev/null\n> +++ b/t/t4018/javascript-function-in-class-2\n> @@ -0,0 +1,11 @@\n> +class Test {\n> +  RIGHT(\n> +      aaaaaaaaaa,\n> +      bbbbbbbbbb,\n> +      cccccccccc,\n> +      dddddddddd\n> +  ) {\n> +    let a = 4;\n> +    let b = ChangeMe;\n> +  }\n> +}\n> diff --git a/t/t4018/javascript-function-in-class-3 b/t/t4018/javascript-function-in-class-3\n> new file mode 100644\n> index 0000000000..e9b20728b2\n> --- /dev/null\n> +++ b/t/t4018/javascript-function-in-class-3\n> @@ -0,0 +1,10 @@\n> +class Test {\n> +  RIGHT(aaaaaaaaaa,\n> +      bbbbbbbbbb,\n> +      cccccccccc,\n> +      dddddddddd\n> +  ) {\n> +    let a = 4;\n> +    let b = ChangeMe;\n> +  }\n> +}\n> diff --git a/t/t4018/javascript-function-in-object-literal b/t/t4018/javascript-function-in-object-literal\n> new file mode 100644\n> index 0000000000..021cc706dd\n> --- /dev/null\n> +++ b/t/t4018/javascript-function-in-object-literal\n> @@ -0,0 +1,7 @@\n> +const obj = {\n> +    RIGHT: function (elems, callback, arg) {\n> +        var length, value;\n> +        // ...\n> +        return ChangeMe\n> +    }\n> +}\n> diff --git a/t/t4018/javascript-generator-function b/t/t4018/javascript-generator-function\n> new file mode 100644\n> index 0000000000..dc7793939f\n> --- /dev/null\n> +++ b/t/t4018/javascript-generator-function\n> @@ -0,0 +1,4 @@\n> +function* RIGHT(a, b) {\n> +  \n> +  return a + b; // ChangeMe\n> +}\n> diff --git a/t/t4018/javascript-generator-function-2 b/t/t4018/javascript-generator-function-2\n> new file mode 100644\n> index 0000000000..950676a612\n> --- /dev/null\n> +++ b/t/t4018/javascript-generator-function-2\n> @@ -0,0 +1,4 @@\n> +function *RIGHT(a, b) {\n> +  \n> +  return a + b; // ChangeMe\n> +}\n> diff --git a/t/t4018/javascript-getter-function-in-class b/t/t4018/javascript-getter-function-in-class\n> new file mode 100644\n> index 0000000000..9a5aee39f7\n> --- /dev/null\n> +++ b/t/t4018/javascript-getter-function-in-class\n> @@ -0,0 +1,6 @@\n> +class Test {\n> +  get RIGHT() {\n> +    let a = 4;\n> +    let b = ChangeMe;\n> +  }\n> +}\n> diff --git a/t/t4018/javascript-setter-function-in-class b/t/t4018/javascript-setter-function-in-class\n> new file mode 100644\n> index 0000000000..dc5f288665\n> --- /dev/null\n> +++ b/t/t4018/javascript-setter-function-in-class\n> @@ -0,0 +1,6 @@\n> +class Test {\n> +  set RIGHT() {\n> +    let a = 4;\n> +    let b = ChangeMe;\n> +  }\n> +}\n> diff --git a/t/t4018/javascript-skip-function-call-statement b/t/t4018/javascript-skip-function-call-statement\n> new file mode 100644\n> index 0000000000..321993c27e\n> --- /dev/null\n> +++ b/t/t4018/javascript-skip-function-call-statement\n> @@ -0,0 +1,7 @@\n> +class Test {\n> +  static RIGHT() {\n> +    haha();\n> +    haha2()\n> +    let b = ChangeMe;\n> +  }\n> +}\n> diff --git a/t/t4018/javascript-skip-keywords b/t/t4018/javascript-skip-keywords\n> new file mode 100644\n> index 0000000000..5584970b58\n> --- /dev/null\n> +++ b/t/t4018/javascript-skip-keywords\n> @@ -0,0 +1,34 @@\n> +function RIGHT(a, b) {\n> +  import(\"./async1\")\n> +  if (a > 1) {\n> +    // ...\n> +  }\n> +  do {\n> +    // ...\n> +  } while (i < 5);\n> +  for (const element of array1) {\n> +    console.log(element)\n> +  }\n> +  with(o) {\n> +    console.log(x)\n> +  }\n> +  switch (expr) {\n> +    case 'a':\n> +      // ...\n> +      break;\n> +    case 'b':\n> +      // ...\n> +      break;\n> +    default:\n> +      // ...\n> +  }\n> +  try {\n> +    // ...\n> +    return (a + c)\n> +  } \n> +  catch (error) {\n> +    // ...\n> +  }\n> +\n> +  return a + b; // ChangeMe\n> +}\n> diff --git a/t/t4018/javascript-static-function-in-class b/t/t4018/javascript-static-function-in-class\n> new file mode 100644\n> index 0000000000..fbf0b7ca3d\n> --- /dev/null\n> +++ b/t/t4018/javascript-static-function-in-class\n> @@ -0,0 +1,6 @@\n> +class Test {\n> +  static RIGHT() {\n> +    let a = 4;\n> +    let b = ChangeMe;\n> +  }\n> +}\n\nThe rest of the test cases look good.\n"},{"id":"451376","messageId":"d8783660-1487-1899-19bf-10654801ea0a@kdbg.org","threadId":"57508","inReplyTo":"kl6lwngwqwm2.fsf@chooglen-macbookpro.roam.corp.google.com","subject":"Re: [GSoC][PATCH v2] Add a diff driver for JavaScript languages.","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2022-03-15T07:40:24Z","receivedAt":"2022-03-15T07:40:35Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 14.03.22 um 18:20 schrieb Glen Choo:\n> xing zhi jiang <a97410985new@gmail.com> writes:\n>> diff --git a/t/t4018/javascript-exports-function b/t/t4018/javascript-exports-function\n>> new file mode 100644\n>> index 0000000000..63b79f5991\n>> --- /dev/null\n>> +++ b/t/t4018/javascript-exports-function\n>> @@ -0,0 +1,4 @@\n>> +exports.RIGHT = function(document) {\n>> +    \n>> +    return ChangeMe;\n>> +}\n> \n> I don't think we should include 'exports.foo = bar'. To my knowledge,\n> this is _not_ a standard ES feature, but rather the CommonJS module\n> system popularized by Node.js [1] and other frameworks. To confirm this,\n> I searched the ES spec and did not find any reference to exports.* [2].\n> \n> Even if we wanted to support nonstandard 'language features' (and this\n> label is tenuous at best, CommonJS is not trying to replace the ES\n> modules standard AFAIK), Node.js is also starting to support ES modules,\n> so I don't think this is a good long term direction for Git.\n\nIt is not a priority to model hunk header regular expressions after some\nstandard and to ignore stuff that is outside the standard. The goal is\nto make them useful in a majority of cases. If there exists a noticable\nchunk of code that uses non-standard constructs, then that is worth\nbeing supported.\n\n> \n> [1] https://nodejs.org/api/modules.html\n> [2] https://262.ecma-international.org/12.0/#sec-exports\n\n-- Hannes\n"},{"id":"451413","messageId":"kl6lee33xd5d.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"57508","inReplyTo":"d8783660-1487-1899-19bf-10654801ea0a@kdbg.org","subject":"Re: [GSoC][PATCH v2] Add a diff driver for JavaScript languages.","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2022-03-15T18:51:26Z","receivedAt":"2022-03-15T18:51:32Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Johannes Sixt <j6t@kdbg.org> writes:\n\n> Am 14.03.22 um 18:20 schrieb Glen Choo:\n>> xing zhi jiang <a97410985new@gmail.com> writes:\n>>> diff --git a/t/t4018/javascript-exports-function b/t/t4018/javascript-exports-function\n>>> new file mode 100644\n>>> index 0000000000..63b79f5991\n>>> --- /dev/null\n>>> +++ b/t/t4018/javascript-exports-function\n>>> @@ -0,0 +1,4 @@\n>>> +exports.RIGHT = function(document) {\n>>> +    \n>>> +    return ChangeMe; >>> +}\n>> \n>> I don't think we should include 'exports.foo = bar'. To my knowledge,\n>> this is _not_ a standard ES feature, but rather the CommonJS module\n>> system popularized by Node.js [1] and other frameworks. To confirm this,\n>> I searched the ES spec and did not find any reference to exports.* [2].\n>> \n>> Even if we wanted to support nonstandard 'language features' (and this\n>> label is tenuous at best, CommonJS is not trying to replace the ES\n>> modules standard AFAIK), Node.js is also starting to support ES modules,\n>> so I don't think this is a good long term direction for Git.\n>\n> It is not a priority to model hunk header regular expressions after some\n> standard and to ignore stuff that is outside the standard. The goal is\n> to make them useful in a majority of cases. If there exists a noticable\n> chunk of code that uses non-standard constructs, then that is worth\n> being supported.\n\nInteresting, I'll take note. I'm still personally not keen on supporting\nCommonJS-only patterns when we are purportedly trying to show diffs for\nJavaScript, but if we think this fits the style, I'm happy to oblige.\n\nSo the question becomes \"Is there a significant amount of code that uses\nthis pattern?\" Probably - this is a fairly common pattern in Node.js\nafter all. But in my experience,\n\n module.exports.RIGHT = function(document) {\n     \n     return ChangeMe;\n }\n\nis even more common. The difference between 'module.exports' and\n'exports' isn't worth going into (StackOverflow has all the answers, for\nthe curious), but if we're taking the approach of supporting CommonJS,\nI'd like to be consistent and also support 'module.exports', i.e.\nperhaps change:\n\n  \"^(exports\\\\.[$_[:alpha:]][$_[:alnum:]]*[\\t ]*=[\\t ]*(\\\\(.*\\\\)|[$_[:alpha:]][$_[:alnum:]]*)[\\t ]*=>.*)\\n\"\n\nto something like:\n\n  \"^((module.)?exports\\\\.[$_[:alpha:]][$_[:alnum:]]*[\\t ]*=[\\t ]*(\\\\(.*\\\\)|[$_[:alpha:]][$_[:alnum:]]*)[\\t ]*=>.*)\\n\"\n"},{"id":"451419","messageId":"xmqq1qz3dnqu.fsf@gitster.g","threadId":"57508","inReplyTo":"kl6lee33xd5d.fsf@chooglen-macbookpro.roam.corp.google.com","subject":"Re: [GSoC][PATCH v2] Add a diff driver for JavaScript languages.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-03-15T19:22:49Z","receivedAt":"2022-03-15T19:22:56Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Glen Choo <chooglen@google.com> writes:\n\n> Interesting, I'll take note. I'm still personally not keen on supporting\n> CommonJS-only patterns when we are purportedly trying to show diffs for\n> JavaScript, but if we think this fits the style, I'm happy to oblige.\n\nThe question is, with these patterns that are aware of CommonJS\nconvention, would your bog-standard-and-boring vanilla JS code be\ndetected incorrectly?  Becoming aware of popular conventions without\nhurting others would be a good thing.\n\nAnd the \"popular conventions\" does not have to be limited to\nCommonJS/Node.\n"},{"id":"451448","messageId":"kl6l8rtayk5o.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"57508","inReplyTo":"xmqq1qz3dnqu.fsf@gitster.g","subject":"Re: [GSoC][PATCH v2] Add a diff driver for JavaScript languages.","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2022-03-15T21:34:43Z","receivedAt":"2022-03-15T21:34:48Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Glen Choo <chooglen@google.com> writes:\n>\n>> Interesting, I'll take note. I'm still personally not keen on supporting\n>> CommonJS-only patterns when we are purportedly trying to show diffs for\n>> JavaScript, but if we think this fits the style, I'm happy to oblige.\n>\n> The question is, with these patterns that are aware of CommonJS\n> convention, would your bog-standard-and-boring vanilla JS code be\n> detected incorrectly?  Becoming aware of popular conventions without\n> hurting others would be a good thing.\n>\n> And the \"popular conventions\" does not have to be limited to\n> CommonJS/Node.\n\nFrom the perspective of \"'exports' is a special name\", yes, we could\ndetect vanilla JS code 'incorrectly' because, in vanilla JS, the names\n'exports' or 'module.exports' are not special. So perhaps, one could\nimagine a browser-side script that deals with \"imports\" and \"exports\" as\npart of their business:\n\n  const exports = {\n    quantity: 1,\n    type: 'boxes',\n  };\n  exports.getQuantity = () => {\n    foo();\n  };\n\nThis diff driver would mistakenly detect `exports.getQuantity = () =>\n{`.\n\nAlthough, the more I think about it, the spirit of this patch seems to\nbe \"we want to show headers whenever we think we are in a function\", so\nwe don't actually need to treat 'exports' or 'module.exports' specially\nat all, e.g. this case should also pass our diff driver tests:\n\n  const foo = {};\n  foo.RIGHT = () => {\n\n    ChangeMe();\n  };\n\nand if we do this, we will correctly handle 'exports' and\n'module.exports' anyway by virtue of them being plain old JS objects.\n"},{"id":"452978","messageId":"CAN_FRN+kN=nQWmH-VhV3Cu7PKcLFJ0uvV=mawR5FWJLTX3_8Qw@mail.gmail.com","threadId":"57508","inReplyTo":"21ca0635-8a70-14ff-6dbc-213eeba85750@kdbg.org","subject":"Re: [GSoC][PATCH v2] Add a diff driver for JavaScript languages.","fromName":"xing zhi jiang","fromEmail":"a97410985new@gmail.com","sentAt":"2022-04-03T13:17:49Z","receivedAt":"2022-04-03T13:18:09Z","isPatch":true,"sender":{"key":"a97410985new@gmail.com","avatar":"https://gravatar.com/avatar/a66532d6438c89cb1989916e4a054b2c0c069296379adef42f9fbfca6afa6b06?d=mp&s=160"},"body":"Sorry for the late reply. I've been busy the last two weeks.\n\nOn Mon, 14 Mar 2022 at 05:54, Johannes Sixt <j6t@kdbg.org> wrote:\n>\n> Am 12.03.22 um 17:48 schrieb xing zhi jiang:\n> > diff --git a/userdiff.c b/userdiff.c\n> > index 8578cb0d12..51bfe4021d 100644\n> > --- a/userdiff.c\n> > +++ b/userdiff.c\n> > @@ -168,6 +168,38 @@ PATTERNS(\"java\",\n> >        \"|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?\"\n> >        \"|[-+*/<>%&^|=!]=\"\n> >        \"|--|\\\\+\\\\+|<<=?|>>>?=?|&&|\\\\|\\\\|\"),\n> > +\n> > +PATTERNS(\"javascript\",\n> > +      /* don't match the expression may contain parenthesis, because it is not a function declaration */\n> > +      \"!^[ \\t]*(if|do|while|for|with|switch|catch|import|return)\\n\"\n> > +      /* don't match statement */\n> > +      \"!;\\n\"\n> > +      /* match normal function */\n> > +      \"^((export[\\t ]+)?(async[\\t ]+)?function[\\t ]*[\\t *]*[$_[:alpha:]][$_[:alnum:]]*[\\t ]*\\\\(.*)\\n\"\n> > +      /* match JavaScript variable declaration with a lambda expression */\n> > +      \"^[\\t ]*((const|let|var)[\\t ]*[$_[:alpha:]][$_[:alnum:]]*[\\t ]*=[\\t ]*\"\n> > +      \"(\\\\(.*\\\\)|[$_[:alpha:]][$_[:alnum:]]*)[\\t ]*=>[\\t ]*\\\\{?)\\n\"\n>\n> It would help readability if this second line of this regex were\n> indented because it is a continuation of the first line.\n>\nThis will be fixed in the v3 patch.\n\n> > +      /* match exports for anonymous fucntion */\n> > +      \"^(exports\\\\.[$_[:alpha:]][$_[:alnum:]]*[\\t ]*=[\\t ]*(\\\\(.*\\\\)|[$_[:alpha:]][$_[:alnum:]]*)[\\t ]*=>.*)\\n\"\n> > +      /* match assign function to LHS */\n> > +      \"^(.*=[\\t ]*function[\\t ]*([$_[:alpha:]][$_[:alnum:]]*)?[\\t ]*\\\\(.*)\\n\"\n>\n> This should be written as\n>\n>          \"^(.*=[\\t ]*function[\\t ]*([$_[:alpha:]][$_[:alnum:]]*[\\t ]*)?\\\\(.*)\\n\"\n>\n> Notice that the whitespace after the identifier can only appear when\n> there is actually an identifier. The point is to reduce the different\n> matches permitted by the sub-expression \"[\\t ]*[\\t ]*\" when there is no\n> identifier in the text.\n>\n> Can the keyword function ever be followed by a number? I guess not. Then\n> [$_[:alpha:]][$_[:alnum:]]* could be reduced to [$_[:alnum:]]+\nThis will be fixed in the v3 patch.\n\n> > +      /* match normal function in object literal */\n> > +      \"^[\\t ]*([$_[:alpha:]][$_[:alnum:]]*[\\t ]*:[\\t ]*function[\\t ].*)\\n\"\n> > +      /* don't match the function in class, which has more than one ident level */\n> > +      \"!^(\\t{2,}|[ ]{5,})\\n\"\n> > +      /* match function in class */\n> > +      \"^[\\t ]*((static[\\t ]+)?((async|get|set)[\\t ]+)?[$_[:alpha:]][$_[:alnum:]]*[\\t ]*\\\\(.*)\",> +    /* word regex */\n> > +      /* hexIntegerLiteral, octalIntegerLiteral, binaryIntegerLiteral, DecimalLiteral and its big version */\n> > +      \"(0[xXoObB])?[0-9a-fA-F][_0-9a-fA-F]*n?\"\n> > +      /* DecimalLiteral may be float */\n> > +      \"|(0|[1-9][_0-9]*)?\\\\.?[0-9][_0-9]*([eE][+-]?[_0-9]+)?\"\n>\n> Having alternatives that begin with an optional part make the regex\n> evaluation comparatively inefficient. In particular, both alternatives\n> above match a decimal integer. I suggest to have the first alternative\n> only for hex, octal, and binary integers, and the second for all decimal\n> numbers including floatingpoint:\n>\n>          /* hexIntegerLiteral, octalIntegerLiteral, binaryIntegerLiteral, and\n> their big versions */\n>          \"0[xXoObB][_0-9a-fA-F]+n?\"\n>          /* DecimalLiteral may be float */\n>          \"|[0-9][_0-9]*(\\\\.[_0-9]*|n)?([eE][+-]?[_0-9]+)?\"\n>\n> and if floating point literals can begin with a decimal point, then we\n> also need\n>\n>          \"|\\\\.[0-9][_0-9]*([eE][+-]?[_0-9]+)?\"\n>\nI agree on separate decimal regex from others. And in JavaScript\nfloating numbers can start with a dot. So need add new regex -\n\"|\\\\.[0-9][_0-9]*([eE][+-]?[_0-9]+)?\".\nThis will be fixed in the v3 patch.\n"},{"id":"452979","messageId":"CAN_FRNK5HKYF3R3+i=CqOkaLx6JxZjw6=VU4_6euYXThSbRf8w@mail.gmail.com","threadId":"57508","inReplyTo":"kl6lee33xd5d.fsf@chooglen-macbookpro.roam.corp.google.com","subject":"Re: [GSoC][PATCH v2] Add a diff driver for JavaScript languages.","fromName":"xing zhi jiang","fromEmail":"a97410985new@gmail.com","sentAt":"2022-04-03T13:20:11Z","receivedAt":"2022-04-03T13:20:27Z","isPatch":true,"sender":{"key":"a97410985new@gmail.com","avatar":"https://gravatar.com/avatar/a66532d6438c89cb1989916e4a054b2c0c069296379adef42f9fbfca6afa6b06?d=mp&s=160"},"body":"On Wed, 16 Mar 2022 at 02:51, Glen Choo <chooglen@google.com> wrote:\n>\n> Johannes Sixt <j6t@kdbg.org> writes:\n> So the question becomes \"Is there a significant amount of code that uses\n> this pattern?\" Probably - this is a fairly common pattern in Node.js\n> after all. But in my experience,\n>\n>  module.exports.RIGHT = function(document) {\n>\n>      return ChangeMe;\n>  }\n>\n> is even more common. The difference between 'module.exports' and\n> 'exports' isn't worth going into (StackOverflow has all the answers, for\n> the curious), but if we're taking the approach of supporting CommonJS,\n> I'd like to be consistent and also support 'module.exports', i.e.\n> perhaps change:\n>\n>   \"^(exports\\\\.[$_[:alpha:]][$_[:alnum:]]*[\\t ]*=[\\t ]*(\\\\(.*\\\\)|[$_[:alpha:]][$_[:alnum:]]*)[\\t ]*=>.*)\\n\"\n>\n> to something like:\n>\n>   \"^((module.)?exports\\\\.[$_[:alpha:]][$_[:alnum:]]*[\\t ]*=[\\t ]*(\\\\(.*\\\\)|[$_[:alpha:]][$_[:alnum:]]*)[\\t ]*=>.*)\\n\"\nI agree on also support the \"module.exports\". This will be applied on\nthe v3 patch\n"},{"id":"452980","messageId":"CAN_FRNKbQAFr5fL-giffKADHjBN2QUt5fUQriTpjOv-g5=9QHw@mail.gmail.com","threadId":"57508","inReplyTo":"kl6lwngwqwm2.fsf@chooglen-macbookpro.roam.corp.google.com","subject":"Re: [GSoC][PATCH v2] Add a diff driver for JavaScript languages.","fromName":"xing zhi jiang","fromEmail":"a97410985new@gmail.com","sentAt":"2022-04-03T13:21:40Z","receivedAt":"2022-04-03T13:21:55Z","isPatch":true,"sender":{"key":"a97410985new@gmail.com","avatar":"https://gravatar.com/avatar/a66532d6438c89cb1989916e4a054b2c0c069296379adef42f9fbfca6afa6b06?d=mp&s=160"},"body":"Sorry for the late reply. Because I was busy last two weeks. And\ngrateful for your review.\n\nOn Tue, 15 Mar 2022 at 01:20, Glen Choo <chooglen@google.com> wrote:\n>\n> > diff --git a/t/t4018/javascript-function b/t/t4018/javascript-function\n> > new file mode 100644\n> > index 0000000000..0cc0bf54e7\n> > --- /dev/null\n> > +++ b/t/t4018/javascript-function\n> > @@ -0,0 +1,4 @@\n> > +function RIGHT(a, b) {\n> > +\n> > +  return a + b; // ChangeMe\n> > +}\n> > diff --git a/t/t4018/javascript-function-2 b/t/t4018/javascript-function-2\n> > new file mode 100644\n> > index 0000000000..06cfb779f0\n> > --- /dev/null\n> > +++ b/t/t4018/javascript-function-2\n> > @@ -0,0 +1,10 @@\n> > +function test(a, b) {\n> > +  return {\n> > +                     RIGHT: function () {\n> > +                             currentUpdateRemovedChunks.forEach(function (chunkId) {\n> > +                                     delete $installedChunks$[chunkId];\n> > +                             });\n> > +                             currentUpdateRemovedChunks = ChangeMe;\n> > +   }\n> > +  }\n> > +}\n>\n> There is also the ES2015 'method shorthand' syntax [3], e.g. `bar` in:\n>\n>   const foo = {\n>     bar() {\n>       console.log('hi');\n>     }\n>   }\n>\n> [3] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Method_definitions\n>\nThe ES2015 method shorthand is already matched by the regex for\nfunction in class\n\"^[\\t ]*((static[\\t ]+)?((async|get|set)[\\t\n]+)?[$_[:alpha:]][$_[:alnum:]]*[\\t ]*\\\\(.*)\"\nSo I update the comment for this regex in the v3 patch. The comment is\n\"/* match function in class and ES5 method shorthand */\"\n\n> > diff --git a/t/t4018/javascript-function-belong-to-IIFE b/t/t4018/javascript-function-belong-to-IIFE\n> > new file mode 100644\n> > index 0000000000..6e5fe858c0\n> > --- /dev/null\n> > +++ b/t/t4018/javascript-function-belong-to-IIFE\n> > @@ -0,0 +1,6 @@\n> > +(function () {\n> > +  this.$RIGHT = function (needle, modifier) {\n> > +      let a = 5;\n> > +      return ChangeMe;\n> > +  };\n> > +}).call(aaaa.prototype);\n>\n>  Does the IIFE matter in this case? This line:\n>\n>   this.$RIGHT = function (needle, modifier) {\n>\n> looks extremely similar to the previous test of `foo = function bar()`.\n>\n> Or perhaps this is meant to demonstrate the edge case of \"matching in a\n> complicated construct\"? If so, perhaps we should test other edge cases\n> like:\n>\n>    function WRONG() {\n>      let RIGHT = function (needle, modifier) {\n>          let a = 5;\n>          return ChangeMe;\n>      };\n>    }\n>\nCurrently, I realize this test case is redundant. Because I trust the\nfunction keyword. So the regex \"^(.*=[\\t ]*function[\\t\n]*([$_[:alnum:]]+[\\t ]*)?\\\\(.*)\\n\" is loose for LHS.\nI will remove this case in the v3 patch.\n"},{"id":"452981","messageId":"CAN_FRNLyH4HB-1MbKGCDFingbaBTu4wmQoxUCrhEA0OhxHYkPg@mail.gmail.com","threadId":"57508","inReplyTo":"kl6l8rtayk5o.fsf@chooglen-macbookpro.roam.corp.google.com","subject":"Re: [GSoC][PATCH v2] Add a diff driver for JavaScript languages.","fromName":"xing zhi jiang","fromEmail":"a97410985new@gmail.com","sentAt":"2022-04-03T13:24:34Z","receivedAt":"2022-04-03T13:24:49Z","isPatch":true,"sender":{"key":"a97410985new@gmail.com","avatar":"https://gravatar.com/avatar/a66532d6438c89cb1989916e4a054b2c0c069296379adef42f9fbfca6afa6b06?d=mp&s=160"},"body":"On Wed, 16 Mar 2022 at 05:34, Glen Choo <chooglen@google.com> wrote:\n>\n> Junio C Hamano <gitster@pobox.com> writes:\n>\n> > Glen Choo <chooglen@google.com> writes:\n> >\n> >> Interesting, I'll take note. I'm still personally not keen on supporting\n> >> CommonJS-only patterns when we are purportedly trying to show diffs for\n> >> JavaScript, but if we think this fits the style, I'm happy to oblige.\n> >\n> > The question is, with these patterns that are aware of CommonJS\n> > convention, would your bog-standard-and-boring vanilla JS code be\n> > detected incorrectly?  Becoming aware of popular conventions without\n> > hurting others would be a good thing.\n> >\n> > And the \"popular conventions\" does not have to be limited to\n> > CommonJS/Node.\n>\n> From the perspective of \"'exports' is a special name\", yes, we could\n> detect vanilla JS code 'incorrectly' because, in vanilla JS, the names\n> 'exports' or 'module.exports' are not special. So perhaps, one could\n> imagine a browser-side script that deals with \"imports\" and \"exports\" as\n> part of their business:\n>\n>   const exports = {\n>     quantity: 1,\n>     type: 'boxes',\n>   };\n>   exports.getQuantity = () => {\n>     foo();\n>   };\n>\n> This diff driver would mistakenly detect `exports.getQuantity = () =>\n> {`.\n>\n> Although, the more I think about it, the spirit of this patch seems to\n> be \"we want to show headers whenever we think we are in a function\", so\n> we don't actually need to treat 'exports' or 'module.exports' specially\n> at all, e.g. this case should also pass our diff driver tests:\n>\n>   const foo = {};\n>   foo.RIGHT = () => {\n>\n>     ChangeMe();\n>   };\n>\n> and if we do this, we will correctly handle 'exports' and\n> 'module.exports' anyway by virtue of them being plain old JS objects.\nThe spirit of this patch is to show headers when we are in the\nfunction body(which has a large code block). Because this can help\nusers understand the context.\nAnd prevent mismatch non-related function. ex:\n    function WRONG() {\n        // ...\n    }\n    const foo = {};\n    foo.RIGHT = () => {\n\n      ChangeMe();\n    };\nif we don't match \"foo.RIGHT = () => {\". It may match the \"function\nWRONG() {\". This would be very misleading.\nSo the v3 patch. I do not treat `exports` as a special keyword. And\ncan match the code like \"foo.RIGHT = () => {\".\nThe regex would be\n\"((module\\\\.)?[$_[:alpha:]][$_[:alnum:]]*\\\\.[$_[:alpha:]][$_[:alnum:]]*[\\t\n]*=[\\t ]*(\\\\(.*\\\\)|[$_[:alpha:]][$_[:alnum:]]*)[\\t ]*=>.*)\"\n"},{"id":"452982","messageId":"20220403132508.28196-1-a97410985new@gmail.com","threadId":"57508","inReplyTo":"20220304130854.857746-1-a97410985new@gmail.com","subject":"[GSoC][PATCH v3] Add a diff driver for JavaScript languages.","fromName":"xing zhi jiang","fromEmail":"a97410985new@gmail.com","sentAt":"2022-04-03T13:25:08Z","receivedAt":"2022-04-03T13:25:16Z","isPatch":true,"sender":{"key":"a97410985new@gmail.com","avatar":"https://gravatar.com/avatar/a66532d6438c89cb1989916e4a054b2c0c069296379adef42f9fbfca6afa6b06?d=mp&s=160"},"body":"In the xfunction part that matches normal functions,\na variable declaration with an assignment of function, the function declaration\nin the class, named export for function in ECMA2015, CommonJS exports for\nnamed function, and also the function is object literal's property[1].\nThe special match is this would match the popular framework test case\npattern(most is same as regex for matching function in class).\n\nAnd in the word regex part, that matches numbers, punctuations, and also the\nJavaScript identifier.\nThis part reference the formal ECMA specification[2].\n\n[1]https://github.com/jquery/jquery/blob/de5398a6ad088dc006b46c6a870a2a053f4cd663/src/core.js#L201\n[2]https://262.ecma-international.org/12.0/#sec-ecmascript-language-lexical-grammar\n\nSigned-off-by: xing zhi jiang <a97410985new@gmail.com>\n---\nI write a simple python script to help me observe the diff's results easier. \nAnd the result is in https://github.com/a97410985/user_diff_observe_tool.\nI observe the six popular JavaScript projects - Jquery, NodeJS, React, TensorflowJS, \nVue, and Webpack. The result is not perfect but acceptable.\nMost of the mismatch is related to the function in class. Ex:\nhttps://github.com/a97410985/user_diff_observe_tool/blob/092bd14c849b9a3f61bb872d730e38b58cc9c303/diff_result/jquery_result.diff#L1094.\nBut this is inevitable.\nAnd besides, apply the code reviews suggestions. I add some new regex.\n1. \"!^[\\t ]*[$_[:alpha:]][$_[:alnum:]][\\t ]*\\\\(.*\\\\)\\\\.\\n\"\n   this is for the case below\n   `\n    function RIGHT(a, b) {\n\n        WRONG(spy.calls.count()).toBe(1)\n        return a + b; // ChangeMe\n    }\n   `\n   if we don't skip chained function call. This would happen mismatch.\n2. \"^[\\t ]*(QUnit.test\\\\(.*)\\n\"\n   I oberve there are many test case pattern in JavaScript. And most of popular JavaScript\n   unit test framework's test case pattern is function call can be matched by the regex, which\n   match \"function in class\". So it is no problem. But `QUnit framework` don't matched by that.\n   Then add this regex into JavaScript userdiff.\n   And the reason includes test case pattern has two\n   1. This is inevitable. Because we can't distinguish that with function in class.\n   2. This can help the user understand what change is happening in which test case. And if \n      don't match the test case, it has the large possibility to match no related function.\n      \nAnd I move the regex, which match normal function in object literal, under the regex \n\"\"!^(\\t{2,}|[ ]{5,})\\n\".\nBecause I found it would many mismatch if function in object literal has too many indent level(\nthe function body usually just few lines of code, and the below has large of code. If has change\non below, it would happen mismatch).\nAnd there are two example for hunk header that match test case pattern.\n      \nRange-diff against v2:\n1:  3b326bd2b6 ! 1:  2791a7b6b8 Add a diff driver for JavaScript languages.\n    @@ Commit message\n     \n         In the xfunction part that matches normal functions,\n         a variable declaration with an assignment of function, the function declaration\n    -    in the class, and also the function is object literal's property[1].\n    +    in the class, named export for function in ECMA2015, CommonJS exports for\n    +    named function, and also the function is object literal's property[1].\n    +    The special match is this would match the popular framework test case\n    +    pattern(most is same as regex for matching function in class).\n     \n         And in the word regex part, that matches numbers, punctuations, and also the\n         JavaScript identifier.\n    @@ t/t4018/javascript-async-function (new)\n     +async function RIGHT(a, b) {\n     +  \n     +  return a + b; // ChangeMe\n    ++}\n    +\n    + ## t/t4018/javascript-es5-method-shorthand (new) ##\n    +@@\n    ++const foo = {\n    ++    RIGHT() {\n    ++        \n    ++        console.log('ChangeMe');\n    ++    }\n     +}\n     \n      ## t/t4018/javascript-export-async-function (new) ##\n    @@ t/t4018/javascript-exports-anomyous-function-2 (new)\n     +exports.RIGHT = (a, b, runtime) => {\n     +\t\n     +    return ChangeMe;\n    ++};\n    +\n    + ## t/t4018/javascript-exports-anomyous-function-3 (new) ##\n    +@@\n    ++module.exports.RIGHT = (a, b, runtime) => {\n    ++\t\n    ++    return ChangeMe;\n    ++};\n    +\n    + ## t/t4018/javascript-exports-anomyous-function-4 (new) ##\n    +@@\n    ++module.exports = ( RIGHT ) => {\n    ++\t\n    ++    return ChangeMe;\n     +};\n     \n      ## t/t4018/javascript-exports-function (new) ##\n    @@ t/t4018/javascript-function (new)\n     +  return a + b; // ChangeMe\n     +}\n     \n    - ## t/t4018/javascript-function-2 (new) ##\n    -@@\n    -+function test(a, b) {\n    -+  return {\n    -+\t\t\tRIGHT: function () {\n    -+\t\t\t\tcurrentUpdateRemovedChunks.forEach(function (chunkId) {\n    -+\t\t\t\t\tdelete $installedChunks$[chunkId];\n    -+\t\t\t\t});\n    -+\t\t\t\tcurrentUpdateRemovedChunks = ChangeMe;\n    -+   }\n    -+  }\n    -+}\n    -\n    - ## t/t4018/javascript-function-belong-to-IIFE (new) ##\n    -@@\n    -+(function () {\n    -+  this.$RIGHT = function (needle, modifier) {\n    -+      let a = 5;\n    -+      return ChangeMe;\n    -+  };\n    -+}).call(aaaa.prototype);\n    -\n      ## t/t4018/javascript-function-in-class (new) ##\n     @@\n     +class Test {\n    @@ t/t4018/javascript-setter-function-in-class (new)\n     +    let a = 4;\n     +    let b = ChangeMe;\n     +  }\n    ++}\n    +\n    + ## t/t4018/javascript-skip-chained-function (new) ##\n    +@@\n    ++function RIGHT(a, b) {\n    ++\n    ++  expect(spy.calls.count()).toBe(1)\n    ++  return a + b; // ChangeMe\n     +}\n     \n      ## t/t4018/javascript-skip-function-call-statement (new) ##\n    @@ userdiff.c: PATTERNS(\"java\",\n     +\t \"!^[ \\t]*(if|do|while|for|with|switch|catch|import|return)\\n\"\n     +\t /* don't match statement */\n     +\t \"!;\\n\"\n    -+\t /* match normal function */\n    ++\t /* match normal function or named export for function in ECMA2015 */\n     +\t \"^((export[\\t ]+)?(async[\\t ]+)?function[\\t ]*[\\t *]*[$_[:alpha:]][$_[:alnum:]]*[\\t ]*\\\\(.*)\\n\"\n    -+\t /* match JavaScript variable declaration with a lambda expression */\n    -+\t \"^[\\t ]*((const|let|var)[\\t ]*[$_[:alpha:]][$_[:alnum:]]*[\\t ]*=[\\t ]*\"\n    -+\t \"(\\\\(.*\\\\)|[$_[:alpha:]][$_[:alnum:]]*)[\\t ]*=>[\\t ]*\\\\{?)\\n\"\n    -+\t /* match exports for anonymous fucntion */\n    -+\t \"^(exports\\\\.[$_[:alpha:]][$_[:alnum:]]*[\\t ]*=[\\t ]*(\\\\(.*\\\\)|[$_[:alpha:]][$_[:alnum:]]*)[\\t ]*=>.*)\\n\"\n    -+\t /* match assign function to LHS */\n    -+\t \"^(.*=[\\t ]*function[\\t ]*([$_[:alpha:]][$_[:alnum:]]*)?[\\t ]*\\\\(.*)\\n\"\n    -+\t /* match normal function in object literal */\n    -+\t \"^[\\t ]*([$_[:alpha:]][$_[:alnum:]]*[\\t ]*:[\\t ]*function[\\t ].*)\\n\"\n    -+\t /* don't match the function in class, which has more than one ident level */\n    ++\t /* match JavaScript variable declaration with a lambda expression at top level */\n    ++\t \"^((const|let|var)[\\t ]*[$_[:alpha:]][$_[:alnum:]]*[\\t ]*=[\\t ]*\"\n    ++\t\t\"(\\\\(.*\\\\)|[$_[:alpha:]][$_[:alnum:]]*)[\\t ]*=>[\\t ]*\\\\{?)\\n\"\n    ++\t /* match object's property assignment by anonymous function and CommonJS exports for named function */\n    ++\t \"^((module\\\\.)?[$_[:alpha:]][$_[:alnum:]]*\\\\.[$_[:alpha:]][$_[:alnum:]]*[\\t ]*=[\\t ]*(async[\\t ]+)?(\\\\(.*\\\\)|[$_[:alpha:]][$_[:alnum:]]*)[\\t ]*=>.*)\\n\"\n    ++\t /* match assign function to LHS with explicit function keyword */\n    ++\t \"^(.*=[\\t ]*function[\\t ]*([$_[:alnum:]]+[\\t ]*)?\\\\(.*)\\n\"\n    ++\t /* popular unit testing framework test case pattern. Most of framework pattern is match by regex for \"function in class\" */\n    ++\t \"^[\\t ]*(QUnit.test\\\\(.*)\\n\"\n    ++\t /* don't match the function in class or in object literal, which has more than one ident level */\n     +\t \"!^(\\t{2,}|[ ]{5,})\\n\"\n    -+\t /* match function in class */\n    ++\t /* match normal function in object literal */\n    ++\t \"^[\\t ]*([$_[:alpha:]][$_[:alnum:]]*[\\t ]*:[\\t ]*function.*)\\n\"\n    ++\t /* don't match chained method call */\n    ++\t \"!^[\\t ]*[$_[:alpha:]][$_[:alnum:]][\\t ]*\\\\(.*\\\\)\\\\.\\n\"\n    ++\t /* match function in class and ES5 method shorthand */\n     +\t \"^[\\t ]*((static[\\t ]+)?((async|get|set)[\\t ]+)?[$_[:alpha:]][$_[:alnum:]]*[\\t ]*\\\\(.*)\",\n     +\t /* word regex */\n    -+\t /* hexIntegerLiteral, octalIntegerLiteral, binaryIntegerLiteral, DecimalLiteral and its big version */\n    -+\t \"(0[xXoObB])?[0-9a-fA-F][_0-9a-fA-F]*n?\"\n    -+\t /* DecimalLiteral may be float */\n    -+\t \"|(0|[1-9][_0-9]*)?\\\\.?[0-9][_0-9]*([eE][+-]?[_0-9]+)?\"\n    ++\t /* hexIntegerLiteral, octalIntegerLiteral, binaryIntegerLiteral, and its big version */\n    ++\t \"0[xXoObB][_0-9a-fA-F]+n?\"\n    ++\t /* DecimalLiteral and its big version*/\n    ++\t \"|[0-9][_0-9]*(\\\\.[0-9][_0-9]*|n)?([eE][+-]?[_0-9]+)?\"\n    ++\t \"|\\\\.[0-9][_0-9]*([eE][+-]?[_0-9]+)?\"\n     +\t /* punctuations */\n     +\t \"|\\\\.{3}|<=|>=|==|!=|={3}|!==|\\\\*{2}|\\\\+{2}|--|<<|>>\"\n     +\t \"|>>>|&&|\\\\|{2}|\\\\?{2}|\\\\+=|-=|\\\\*=|%=|\\\\*{2}=\"\n\n Documentation/gitattributes.txt               |  2 +\n ...avascript-assignment-of-anonymous-function |  4 ++\n .../javascript-assignment-of-arrow-function   |  4 ++\n .../javascript-assignment-of-arrow-function-2 |  4 ++\n .../javascript-assignment-of-arrow-function-3 |  4 ++\n .../javascript-assignment-of-named-function   |  4 ++\n t/t4018/javascript-async-function             |  4 ++\n t/t4018/javascript-es5-method-shorthand       |  6 +++\n t/t4018/javascript-export-async-function      |  4 ++\n t/t4018/javascript-export-function            |  4 ++\n t/t4018/javascript-exports-anomyous-function  |  4 ++\n .../javascript-exports-anomyous-function-2    |  4 ++\n .../javascript-exports-anomyous-function-3    |  4 ++\n .../javascript-exports-anomyous-function-4    |  4 ++\n t/t4018/javascript-exports-function           |  4 ++\n t/t4018/javascript-function                   |  4 ++\n t/t4018/javascript-function-in-class          |  6 +++\n t/t4018/javascript-function-in-class-2        | 11 ++++\n t/t4018/javascript-function-in-class-3        | 10 ++++\n t/t4018/javascript-function-in-object-literal |  7 +++\n t/t4018/javascript-generator-function         |  4 ++\n t/t4018/javascript-generator-function-2       |  4 ++\n t/t4018/javascript-getter-function-in-class   |  6 +++\n t/t4018/javascript-setter-function-in-class   |  6 +++\n t/t4018/javascript-skip-chained-function      |  5 ++\n .../javascript-skip-function-call-statement   |  7 +++\n t/t4018/javascript-skip-keywords              | 34 ++++++++++++\n t/t4018/javascript-static-function-in-class   |  6 +++\n t/t4034-diff-words.sh                         |  1 +\n t/t4034/javascript/expect                     | 54 +++++++++++++++++++\n t/t4034/javascript/post                       | 33 ++++++++++++\n t/t4034/javascript/pre                        | 33 ++++++++++++\n userdiff.c                                    | 37 +++++++++++++\n 33 files changed, 328 insertions(+)\n create mode 100644 t/t4018/javascript-assignment-of-anonymous-function\n create mode 100644 t/t4018/javascript-assignment-of-arrow-function\n create mode 100644 t/t4018/javascript-assignment-of-arrow-function-2\n create mode 100644 t/t4018/javascript-assignment-of-arrow-function-3\n create mode 100644 t/t4018/javascript-assignment-of-named-function\n create mode 100644 t/t4018/javascript-async-function\n create mode 100644 t/t4018/javascript-es5-method-shorthand\n create mode 100644 t/t4018/javascript-export-async-function\n create mode 100644 t/t4018/javascript-export-function\n create mode 100644 t/t4018/javascript-exports-anomyous-function\n create mode 100644 t/t4018/javascript-exports-anomyous-function-2\n create mode 100644 t/t4018/javascript-exports-anomyous-function-3\n create mode 100644 t/t4018/javascript-exports-anomyous-function-4\n create mode 100644 t/t4018/javascript-exports-function\n create mode 100644 t/t4018/javascript-function\n create mode 100644 t/t4018/javascript-function-in-class\n create mode 100644 t/t4018/javascript-function-in-class-2\n create mode 100644 t/t4018/javascript-function-in-class-3\n create mode 100644 t/t4018/javascript-function-in-object-literal\n create mode 100644 t/t4018/javascript-generator-function\n create mode 100644 t/t4018/javascript-generator-function-2\n create mode 100644 t/t4018/javascript-getter-function-in-class\n create mode 100644 t/t4018/javascript-setter-function-in-class\n create mode 100644 t/t4018/javascript-skip-chained-function\n create mode 100644 t/t4018/javascript-skip-function-call-statement\n create mode 100644 t/t4018/javascript-skip-keywords\n create mode 100644 t/t4018/javascript-static-function-in-class\n create mode 100644 t/t4034/javascript/expect\n create mode 100644 t/t4034/javascript/post\n create mode 100644 t/t4034/javascript/pre\n\ndiff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\nindex 60984a4682..a8e3e4d735 100644\n--- a/Documentation/gitattributes.txt\n+++ b/Documentation/gitattributes.txt\n@@ -828,6 +828,8 @@ patterns are available:\n \n - `java` suitable for source code in the Java language.\n \n+- `javascript` suitable for source code in the JavaScript language.\n+\n - `markdown` suitable for Markdown documents.\n \n - `matlab` suitable for source code in the MATLAB and Octave languages.\ndiff --git a/t/t4018/javascript-assignment-of-anonymous-function b/t/t4018/javascript-assignment-of-anonymous-function\nnew file mode 100644\nindex 0000000000..b6f2ccccfc\n--- /dev/null\n+++ b/t/t4018/javascript-assignment-of-anonymous-function\n@@ -0,0 +1,4 @@\n+const RIGHT = function (a, b) {\n+\t\n+    return a + b; // ChangeMe\n+};\ndiff --git a/t/t4018/javascript-assignment-of-arrow-function b/t/t4018/javascript-assignment-of-arrow-function\nnew file mode 100644\nindex 0000000000..24ce517b7a\n--- /dev/null\n+++ b/t/t4018/javascript-assignment-of-arrow-function\n@@ -0,0 +1,4 @@\n+const RIGHT = (a, b) => {\n+\t\n+    return a + b; // ChangeMe\n+};\ndiff --git a/t/t4018/javascript-assignment-of-arrow-function-2 b/t/t4018/javascript-assignment-of-arrow-function-2\nnew file mode 100644\nindex 0000000000..bbf5de369e\n--- /dev/null\n+++ b/t/t4018/javascript-assignment-of-arrow-function-2\n@@ -0,0 +1,4 @@\n+const RIGHT = (a, b)=>{\n+\t\n+    return a + b; // ChangeMe\n+};\ndiff --git a/t/t4018/javascript-assignment-of-arrow-function-3 b/t/t4018/javascript-assignment-of-arrow-function-3\nnew file mode 100644\nindex 0000000000..4a07aa3259\n--- /dev/null\n+++ b/t/t4018/javascript-assignment-of-arrow-function-3\n@@ -0,0 +1,4 @@\n+const RIGHT=test=>{\n+\t\n+    return test + 1; // ChangeMe\n+};\ndiff --git a/t/t4018/javascript-assignment-of-named-function b/t/t4018/javascript-assignment-of-named-function\nnew file mode 100644\nindex 0000000000..bfc486ebef\n--- /dev/null\n+++ b/t/t4018/javascript-assignment-of-named-function\n@@ -0,0 +1,4 @@\n+const RIGHT = function test (a, b) {\n+\t\n+    return a + b; // ChangeMe\n+};\ndiff --git a/t/t4018/javascript-async-function b/t/t4018/javascript-async-function\nnew file mode 100644\nindex 0000000000..993e6926bf\n--- /dev/null\n+++ b/t/t4018/javascript-async-function\n@@ -0,0 +1,4 @@\n+async function RIGHT(a, b) {\n+  \n+  return a + b; // ChangeMe\n+}\ndiff --git a/t/t4018/javascript-es5-method-shorthand b/t/t4018/javascript-es5-method-shorthand\nnew file mode 100644\nindex 0000000000..01fc0cc34b\n--- /dev/null\n+++ b/t/t4018/javascript-es5-method-shorthand\n@@ -0,0 +1,6 @@\n+const foo = {\n+    RIGHT() {\n+        \n+        console.log('ChangeMe');\n+    }\n+}\ndiff --git a/t/t4018/javascript-export-async-function b/t/t4018/javascript-export-async-function\nnew file mode 100644\nindex 0000000000..fecbd669d7\n--- /dev/null\n+++ b/t/t4018/javascript-export-async-function\n@@ -0,0 +1,4 @@\n+export async function RIGHT(a, b) {\n+  \n+  return a + b; // ChangeMe\n+}\ndiff --git a/t/t4018/javascript-export-function b/t/t4018/javascript-export-function\nnew file mode 100644\nindex 0000000000..b5acbb2b08\n--- /dev/null\n+++ b/t/t4018/javascript-export-function\n@@ -0,0 +1,4 @@\n+export function RIGHT(a, b) {\n+  \n+  return a + b; // ChangeMe\n+}\ndiff --git a/t/t4018/javascript-exports-anomyous-function b/t/t4018/javascript-exports-anomyous-function\nnew file mode 100644\nindex 0000000000..6786cbda8d\n--- /dev/null\n+++ b/t/t4018/javascript-exports-anomyous-function\n@@ -0,0 +1,4 @@\n+exports.setFlagged = RIGHT => {\n+\t\n+    return ChangeMe;\n+};\ndiff --git a/t/t4018/javascript-exports-anomyous-function-2 b/t/t4018/javascript-exports-anomyous-function-2\nnew file mode 100644\nindex 0000000000..883569f40d\n--- /dev/null\n+++ b/t/t4018/javascript-exports-anomyous-function-2\n@@ -0,0 +1,4 @@\n+exports.RIGHT = (a, b, runtime) => {\n+\t\n+    return ChangeMe;\n+};\ndiff --git a/t/t4018/javascript-exports-anomyous-function-3 b/t/t4018/javascript-exports-anomyous-function-3\nnew file mode 100644\nindex 0000000000..bc78ed87eb\n--- /dev/null\n+++ b/t/t4018/javascript-exports-anomyous-function-3\n@@ -0,0 +1,4 @@\n+module.exports.RIGHT = (a, b, runtime) => {\n+\t\n+    return ChangeMe;\n+};\ndiff --git a/t/t4018/javascript-exports-anomyous-function-4 b/t/t4018/javascript-exports-anomyous-function-4\nnew file mode 100644\nindex 0000000000..b952cf2a5a\n--- /dev/null\n+++ b/t/t4018/javascript-exports-anomyous-function-4\n@@ -0,0 +1,4 @@\n+module.exports = ( RIGHT ) => {\n+\t\n+    return ChangeMe;\n+};\ndiff --git a/t/t4018/javascript-exports-function b/t/t4018/javascript-exports-function\nnew file mode 100644\nindex 0000000000..63b79f5991\n--- /dev/null\n+++ b/t/t4018/javascript-exports-function\n@@ -0,0 +1,4 @@\n+exports.RIGHT = function(document) {\n+    \n+    return ChangeMe;\n+}\ndiff --git a/t/t4018/javascript-function b/t/t4018/javascript-function\nnew file mode 100644\nindex 0000000000..0cc0bf54e7\n--- /dev/null\n+++ b/t/t4018/javascript-function\n@@ -0,0 +1,4 @@\n+function RIGHT(a, b) {\n+\n+  return a + b; // ChangeMe\n+}\ndiff --git a/t/t4018/javascript-function-in-class b/t/t4018/javascript-function-in-class\nnew file mode 100644\nindex 0000000000..0cc0a26612\n--- /dev/null\n+++ b/t/t4018/javascript-function-in-class\n@@ -0,0 +1,6 @@\n+class Test {\n+  RIGHT() {\n+    let a = 4;\n+    let b = ChangeMe;\n+  }\n+}\ndiff --git a/t/t4018/javascript-function-in-class-2 b/t/t4018/javascript-function-in-class-2\nnew file mode 100644\nindex 0000000000..725495fe55\n--- /dev/null\n+++ b/t/t4018/javascript-function-in-class-2\n@@ -0,0 +1,11 @@\n+class Test {\n+  RIGHT(\n+      aaaaaaaaaa,\n+      bbbbbbbbbb,\n+      cccccccccc,\n+      dddddddddd\n+  ) {\n+    let a = 4;\n+    let b = ChangeMe;\n+  }\n+}\ndiff --git a/t/t4018/javascript-function-in-class-3 b/t/t4018/javascript-function-in-class-3\nnew file mode 100644\nindex 0000000000..e9b20728b2\n--- /dev/null\n+++ b/t/t4018/javascript-function-in-class-3\n@@ -0,0 +1,10 @@\n+class Test {\n+  RIGHT(aaaaaaaaaa,\n+      bbbbbbbbbb,\n+      cccccccccc,\n+      dddddddddd\n+  ) {\n+    let a = 4;\n+    let b = ChangeMe;\n+  }\n+}\ndiff --git a/t/t4018/javascript-function-in-object-literal b/t/t4018/javascript-function-in-object-literal\nnew file mode 100644\nindex 0000000000..021cc706dd\n--- /dev/null\n+++ b/t/t4018/javascript-function-in-object-literal\n@@ -0,0 +1,7 @@\n+const obj = {\n+    RIGHT: function (elems, callback, arg) {\n+        var length, value;\n+        // ...\n+        return ChangeMe\n+    }\n+}\ndiff --git a/t/t4018/javascript-generator-function b/t/t4018/javascript-generator-function\nnew file mode 100644\nindex 0000000000..dc7793939f\n--- /dev/null\n+++ b/t/t4018/javascript-generator-function\n@@ -0,0 +1,4 @@\n+function* RIGHT(a, b) {\n+  \n+  return a + b; // ChangeMe\n+}\ndiff --git a/t/t4018/javascript-generator-function-2 b/t/t4018/javascript-generator-function-2\nnew file mode 100644\nindex 0000000000..950676a612\n--- /dev/null\n+++ b/t/t4018/javascript-generator-function-2\n@@ -0,0 +1,4 @@\n+function *RIGHT(a, b) {\n+  \n+  return a + b; // ChangeMe\n+}\ndiff --git a/t/t4018/javascript-getter-function-in-class b/t/t4018/javascript-getter-function-in-class\nnew file mode 100644\nindex 0000000000..9a5aee39f7\n--- /dev/null\n+++ b/t/t4018/javascript-getter-function-in-class\n@@ -0,0 +1,6 @@\n+class Test {\n+  get RIGHT() {\n+    let a = 4;\n+    let b = ChangeMe;\n+  }\n+}\ndiff --git a/t/t4018/javascript-setter-function-in-class b/t/t4018/javascript-setter-function-in-class\nnew file mode 100644\nindex 0000000000..dc5f288665\n--- /dev/null\n+++ b/t/t4018/javascript-setter-function-in-class\n@@ -0,0 +1,6 @@\n+class Test {\n+  set RIGHT() {\n+    let a = 4;\n+    let b = ChangeMe;\n+  }\n+}\ndiff --git a/t/t4018/javascript-skip-chained-function b/t/t4018/javascript-skip-chained-function\nnew file mode 100644\nindex 0000000000..4ce23901cf\n--- /dev/null\n+++ b/t/t4018/javascript-skip-chained-function\n@@ -0,0 +1,5 @@\n+function RIGHT(a, b) {\n+\n+  expect(spy.calls.count()).toBe(1)\n+  return a + b; // ChangeMe\n+}\ndiff --git a/t/t4018/javascript-skip-function-call-statement b/t/t4018/javascript-skip-function-call-statement\nnew file mode 100644\nindex 0000000000..321993c27e\n--- /dev/null\n+++ b/t/t4018/javascript-skip-function-call-statement\n@@ -0,0 +1,7 @@\n+class Test {\n+  static RIGHT() {\n+    haha();\n+    haha2()\n+    let b = ChangeMe;\n+  }\n+}\ndiff --git a/t/t4018/javascript-skip-keywords b/t/t4018/javascript-skip-keywords\nnew file mode 100644\nindex 0000000000..5584970b58\n--- /dev/null\n+++ b/t/t4018/javascript-skip-keywords\n@@ -0,0 +1,34 @@\n+function RIGHT(a, b) {\n+  import(\"./async1\")\n+  if (a > 1) {\n+    // ...\n+  }\n+  do {\n+    // ...\n+  } while (i < 5);\n+  for (const element of array1) {\n+    console.log(element)\n+  }\n+  with(o) {\n+    console.log(x)\n+  }\n+  switch (expr) {\n+    case 'a':\n+      // ...\n+      break;\n+    case 'b':\n+      // ...\n+      break;\n+    default:\n+      // ...\n+  }\n+  try {\n+    // ...\n+    return (a + c)\n+  } \n+  catch (error) {\n+    // ...\n+  }\n+\n+  return a + b; // ChangeMe\n+}\ndiff --git a/t/t4018/javascript-static-function-in-class b/t/t4018/javascript-static-function-in-class\nnew file mode 100644\nindex 0000000000..fbf0b7ca3d\n--- /dev/null\n+++ b/t/t4018/javascript-static-function-in-class\n@@ -0,0 +1,6 @@\n+class Test {\n+  static RIGHT() {\n+    let a = 4;\n+    let b = ChangeMe;\n+  }\n+}\ndiff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh\nindex d5abcf4b4c..33073edeca 100755\n--- a/t/t4034-diff-words.sh\n+++ b/t/t4034-diff-words.sh\n@@ -324,6 +324,7 @@ test_language_driver dts\n test_language_driver fortran\n test_language_driver html\n test_language_driver java\n+test_language_driver javascript\n test_language_driver matlab\n test_language_driver objc\n test_language_driver pascal\ndiff --git a/t/t4034/javascript/expect b/t/t4034/javascript/expect\nnew file mode 100644\nindex 0000000000..419d61903b\n--- /dev/null\n+++ b/t/t4034/javascript/expect\n@@ -0,0 +1,54 @@\n+<BOLD>diff --git a/pre b/post<RESET>\n+<BOLD>index 18f4796..46f9b62 100644<RESET>\n+<BOLD>--- a/pre<RESET>\n+<BOLD>+++ b/post<RESET>\n+<CYAN>@@ -1,33 +1,33 @@<RESET>\n+// DecimalLiteral<RESET>\n+<RED>123<RESET>\n+<RED>0.123<RESET>\n+<RED>.123<RESET>\n+<RED>0.123e+5<RESET>\n+<RED>0.123E+5<RESET>\n+<RED>0.123e5<RESET>\n+<RED>1222222222222222223334444n<RESET><GREEN>124<RESET>\n+<GREEN>0.124<RESET>\n+<GREEN>.124<RESET>\n+<GREEN>0.123e-5<RESET>\n+<GREEN>0.123E-5<RESET>\n+<GREEN>0.123E5<RESET>\n+<GREEN>12222222222222222233344445n<RESET>\n+// HexIntegerLiteral<RESET>\n+<RED>0x10<RESET>\n+<RED>0X6Fa1<RESET>\n+<RED>0x123_456<RESET>\n+<RED>0x1234182989812f1289an<RESET><GREEN>0x11<RESET>\n+<GREEN>0X5Fa1<RESET>\n+<GREEN>0x123_756<RESET>\n+<GREEN>0x1234182989812f1289bn<RESET>\n+// OctalIntegerLiteral<RESET>\n+<RED>05<RESET>\n+<RED>0o6<RESET>\n+<RED>0O7<RESET>\n+<RED>0512_567<RESET>\n+<RED>0o424242424242424242424242424242666666n<RESET><GREEN>06<RESET>\n+<GREEN>0o5<RESET>\n+<GREEN>0O4<RESET>\n+<GREEN>0511_567<RESET>\n+<GREEN>0o424242424242424242424242424242666667n<RESET>\n+// BinaryIntegerLiteral<RESET>\n+<RED>0b1001<RESET>\n+<RED>0B0110<RESET>\n+<RED>0b0001_1001_0011<RESET>\n+<RED>0b1111111111111111111111111111111111111n<RESET><GREEN>0b1101<RESET>\n+<GREEN>0B0010<RESET>\n+<GREEN>0b0001_1101_0011<RESET>\n+<GREEN>0b11111111111111000011111111111111111n<RESET>\n+// punctuations<RESET>\n+{<RED>a<RESET><GREEN>b<RESET>} (<RED>a<RESET><GREEN>b<RESET>)\n+<RED>a<RESET><GREEN>b<RESET>;\n+[<RED>1,<RESET>2<GREEN>,3<RESET>]\n+[<RED>1, 2,<RESET> ...<RED>params<RESET><GREEN>params_v2<RESET> ]\n+a<RED><=<RESET><GREEN>=<RESET>2 a<RED>>=<RESET><GREEN>=<RESET>2 a<RED>==<RESET><GREEN>=<RESET>2 a<RED>!=<RESET><GREEN>=<RESET>2 a<RED>===<RESET><GREEN>=<RESET>2 a<RED>!==<RESET><GREEN>=<RESET>2 a<RED>^=<RESET><GREEN>=<RESET>2 a<RED>=><RESET><GREEN>=<RESET>2\n+a<RED>+=<RESET><GREEN>-=<RESET>b a<RED>*=<RESET><GREEN>%=<RESET>b a<RED>**=<RESET><GREEN>&&=<RESET>b a<RED>||=<RESET><GREEN>|=<RESET>b\n+b<RED>+<RESET><GREEN>-<RESET>c a<RED>--<RESET><GREEN>++<RESET> a<RED>>><RESET><GREEN><<<RESET>b a<RED>>>><RESET><GREEN>>>>=<RESET>b a<RED>>>=<RESET><GREEN><<=<RESET>b\n+a<RED>&&<RESET><GREEN>&<RESET>b a<RED>||<RESET><GREEN>|<RESET>b a<RED>&&=<RESET><GREEN>??=<RESET>b\ndiff --git a/t/t4034/javascript/post b/t/t4034/javascript/post\nnew file mode 100644\nindex 0000000000..46f9b627e4\n--- /dev/null\n+++ b/t/t4034/javascript/post\n@@ -0,0 +1,33 @@\n+// DecimalLiteral\n+124\n+0.124\n+.124\n+0.123e-5\n+0.123E-5\n+0.123E5\n+12222222222222222233344445n\n+// HexIntegerLiteral\n+0x11\n+0X5Fa1\n+0x123_756\n+0x1234182989812f1289bn\n+// OctalIntegerLiteral\n+06\n+0o5\n+0O4\n+0511_567\n+0o424242424242424242424242424242666667n\n+// BinaryIntegerLiteral\n+0b1101\n+0B0010\n+0b0001_1101_0011\n+0b11111111111111000011111111111111111n\n+// punctuations\n+{b} (b)\n+b;\n+[2,3]\n+[ ...params_v2 ]\n+a=2 a=2 a=2 a=2 a=2 a=2 a=2 a=2\n+a-=b a%=b a&&=b a|=b\n+b-c a++ a<<b a>>>=b a<<=b\n+a&b a|b a??=b\ndiff --git a/t/t4034/javascript/pre b/t/t4034/javascript/pre\nnew file mode 100644\nindex 0000000000..18f479688c\n--- /dev/null\n+++ b/t/t4034/javascript/pre\n@@ -0,0 +1,33 @@\n+// DecimalLiteral\n+123\n+0.123\n+.123\n+0.123e+5\n+0.123E+5\n+0.123e5\n+1222222222222222223334444n\n+// HexIntegerLiteral\n+0x10\n+0X6Fa1\n+0x123_456\n+0x1234182989812f1289an\n+// OctalIntegerLiteral\n+05\n+0o6\n+0O7\n+0512_567\n+0o424242424242424242424242424242666666n\n+// BinaryIntegerLiteral\n+0b1001\n+0B0110\n+0b0001_1001_0011\n+0b1111111111111111111111111111111111111n\n+// punctuations\n+{a} (a)\n+a;\n+[1,2]\n+[ 1, 2, ...params ]\n+a<=2 a>=2 a==2 a!=2 a===2 a!==2 a^=2 a=>2\n+a+=b a*=b a**=b a||=b\n+b+c a-- a>>b a>>>b a>>=b\n+a&&b a||b a&&=b\ndiff --git a/userdiff.c b/userdiff.c\nindex 8578cb0d12..306286df35 100644\n--- a/userdiff.c\n+++ b/userdiff.c\n@@ -168,6 +168,43 @@ PATTERNS(\"java\",\n \t \"|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?\"\n \t \"|[-+*/<>%&^|=!]=\"\n \t \"|--|\\\\+\\\\+|<<=?|>>>?=?|&&|\\\\|\\\\|\"),\n+\n+PATTERNS(\"javascript\",\n+\t /* don't match the expression may contain parenthesis, because it is not a function declaration */\n+\t \"!^[ \\t]*(if|do|while|for|with|switch|catch|import|return)\\n\"\n+\t /* don't match statement */\n+\t \"!;\\n\"\n+\t /* match normal function or named export for function in ECMA2015 */\n+\t \"^((export[\\t ]+)?(async[\\t ]+)?function[\\t ]*[\\t *]*[$_[:alpha:]][$_[:alnum:]]*[\\t ]*\\\\(.*)\\n\"\n+\t /* match JavaScript variable declaration with a lambda expression at top level */\n+\t \"^((const|let|var)[\\t ]*[$_[:alpha:]][$_[:alnum:]]*[\\t ]*=[\\t ]*\"\n+\t\t\"(\\\\(.*\\\\)|[$_[:alpha:]][$_[:alnum:]]*)[\\t ]*=>[\\t ]*\\\\{?)\\n\"\n+\t /* match object's property assignment by anonymous function and CommonJS exports for named function */\n+\t \"^((module\\\\.)?[$_[:alpha:]][$_[:alnum:]]*\\\\.[$_[:alpha:]][$_[:alnum:]]*[\\t ]*=[\\t ]*(async[\\t ]+)?(\\\\(.*\\\\)|[$_[:alpha:]][$_[:alnum:]]*)[\\t ]*=>.*)\\n\"\n+\t /* match assign function to LHS with explicit function keyword */\n+\t \"^(.*=[\\t ]*function[\\t ]*([$_[:alnum:]]+[\\t ]*)?\\\\(.*)\\n\"\n+\t /* popular unit testing framework test case pattern. Most of framework pattern is match by regex for \"function in class\" */\n+\t \"^[\\t ]*(QUnit.test\\\\(.*)\\n\"\n+\t /* don't match the function in class or in object literal, which has more than one ident level */\n+\t \"!^(\\t{2,}|[ ]{5,})\\n\"\n+\t /* match normal function in object literal */\n+\t \"^[\\t ]*([$_[:alpha:]][$_[:alnum:]]*[\\t ]*:[\\t ]*function.*)\\n\"\n+\t /* don't match chained method call */\n+\t \"!^[\\t ]*[$_[:alpha:]][$_[:alnum:]][\\t ]*\\\\(.*\\\\)\\\\.\\n\"\n+\t /* match function in class and ES5 method shorthand */\n+\t \"^[\\t ]*((static[\\t ]+)?((async|get|set)[\\t ]+)?[$_[:alpha:]][$_[:alnum:]]*[\\t ]*\\\\(.*)\",\n+\t /* word regex */\n+\t /* hexIntegerLiteral, octalIntegerLiteral, binaryIntegerLiteral, and its big version */\n+\t \"0[xXoObB][_0-9a-fA-F]+n?\"\n+\t /* DecimalLiteral and its big version*/\n+\t \"|[0-9][_0-9]*(\\\\.[0-9][_0-9]*|n)?([eE][+-]?[_0-9]+)?\"\n+\t \"|\\\\.[0-9][_0-9]*([eE][+-]?[_0-9]+)?\"\n+\t /* punctuations */\n+\t \"|\\\\.{3}|<=|>=|==|!=|={3}|!==|\\\\*{2}|\\\\+{2}|--|<<|>>\"\n+\t \"|>>>|&&|\\\\|{2}|\\\\?{2}|\\\\+=|-=|\\\\*=|%=|\\\\*{2}=\"\n+\t \"|<<=|>>=|>>>=|&=|\\\\|=|\\\\^=|&&=|\\\\|{2}=|\\\\?{2}=|=>\"\n+\t /* identifiers */\n+\t \"|[$_[:alpha:]][$_[:alnum:]]*\"),\n PATTERNS(\"markdown\",\n \t \"^ {0,3}#{1,6}[ \\t].*\",\n \t /* -- */\n-- \n2.35.1.273.ge6ebfd0e8c.dirty\n\n"},{"id":"452990","messageId":"595d535d-3d3e-9994-0b80-4521a753c74c@kdbg.org","threadId":"57508","inReplyTo":"20220403132508.28196-1-a97410985new@gmail.com","subject":"Re: [GSoC][PATCH v3] Add a diff driver for JavaScript languages.","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2022-04-03T14:40:21Z","receivedAt":"2022-04-03T14:40:29Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 03.04.22 um 15:25 schrieb xing zhi jiang:\n> In the xfunction part that matches normal functions,\n> a variable declaration with an assignment of function, the function declaration\n> in the class, named export for function in ECMA2015, CommonJS exports for\n> named function, and also the function is object literal's property[1].\n> The special match is this would match the popular framework test case\n> pattern(most is same as regex for matching function in class).\n> \n> And in the word regex part, that matches numbers, punctuations, and also the\n> JavaScript identifier.\n> This part reference the formal ECMA specification[2].\n> \n> [1]https://github.com/jquery/jquery/blob/de5398a6ad088dc006b46c6a870a2a053f4cd663/src/core.js#L201\n> [2]https://262.ecma-international.org/12.0/#sec-ecmascript-language-lexical-grammar\n> \n> Signed-off-by: xing zhi jiang <a97410985new@gmail.com>\n> ---\n> I write a simple python script to help me observe the diff's results easier. \n> And the result is in https://github.com/a97410985/user_diff_observe_tool.\n> I observe the six popular JavaScript projects - Jquery, NodeJS, React, TensorflowJS, \n> Vue, and Webpack. The result is not perfect but acceptable.\n> Most of the mismatch is related to the function in class. Ex:\n> https://github.com/a97410985/user_diff_observe_tool/blob/092bd14c849b9a3f61bb872d730e38b58cc9c303/diff_result/jquery_result.diff#L1094.\n> But this is inevitable.\n> And besides, apply the code reviews suggestions. I add some new regex.\n> 1. \"!^[\\t ]*[$_[:alpha:]][$_[:alnum:]][\\t ]*\\\\(.*\\\\)\\\\.\\n\"\n>    this is for the case below\n>    `\n>     function RIGHT(a, b) {\n> \n>         WRONG(spy.calls.count()).toBe(1)\n>         return a + b; // ChangeMe\n>     }\n>    `\n>    if we don't skip chained function call. This would happen mismatch.\n\nOK, but see below.\n\n> 2. \"^[\\t ]*(QUnit.test\\\\(.*)\\n\"\n>    I oberve there are many test case pattern in JavaScript. And most of popular JavaScript\n>    unit test framework's test case pattern is function call can be matched by the regex, which\n>    match \"function in class\". So it is no problem. But `QUnit framework` don't matched by that.\n>    Then add this regex into JavaScript userdiff.\n>    And the reason includes test case pattern has two\n>    1. This is inevitable. Because we can't distinguish that with function in class.\n>    2. This can help the user understand what change is happening in which test case. And if \n>       don't match the test case, it has the large possibility to match no related function.\n\nGood. I consider this like matching a language keyword. If additional\npatterns of this kind occur, they can be added later, but we must make\nsure that this does not proliferate.\n\n>       \n> And I move the regex, which match normal function in object literal, under the regex \n> \"\"!^(\\t{2,}|[ ]{5,})\\n\".\n> Because I found it would many mismatch if function in object literal has too many indent level(\n> the function body usually just few lines of code, and the below has large of code. If has change\n> on below, it would happen mismatch).\n> And there are two example for hunk header that match test case pattern.\n\nOK.\n\n> diff --git a/t/t4018/javascript-skip-chained-function b/t/t4018/javascript-skip-chained-function\n> new file mode 100644\n> index 0000000000..4ce23901cf\n> --- /dev/null\n> +++ b/t/t4018/javascript-skip-chained-function\n> @@ -0,0 +1,5 @@\n> +function RIGHT(a, b) {\n> +\n> +  expect(spy.calls.count()).toBe(1)\n> +  return a + b; // ChangeMe\n> +}\n\nThis is the test case that attempts to make sure that the chained\nfunction calls are not matched. But since there is no line between\nChangeMe and the not-to-be-matched line, this test case would not detect\nsuch an incorrect match. You have to insert a line between expect... and\nreturn...ChangeMe\n\n> diff --git a/userdiff.c b/userdiff.c\n> index 8578cb0d12..306286df35 100644\n> --- a/userdiff.c\n> +++ b/userdiff.c\n> @@ -168,6 +168,43 @@ PATTERNS(\"java\",\n>  \t \"|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?\"\n>  \t \"|[-+*/<>%&^|=!]=\"\n>  \t \"|--|\\\\+\\\\+|<<=?|>>>?=?|&&|\\\\|\\\\|\"),\n> +\n> +PATTERNS(\"javascript\",\n> +\t /* don't match the expression may contain parenthesis, because it is not a function declaration */\n> +\t \"!^[ \\t]*(if|do|while|for|with|switch|catch|import|return)\\n\"\n> +\t /* don't match statement */\n> +\t \"!;\\n\"\n> +\t /* match normal function or named export for function in ECMA2015 */\n> +\t \"^((export[\\t ]+)?(async[\\t ]+)?function[\\t ]*[\\t *]*[$_[:alpha:]][$_[:alnum:]]*[\\t ]*\\\\(.*)\\n\"\n> +\t /* match JavaScript variable declaration with a lambda expression at top level */\n> +\t \"^((const|let|var)[\\t ]*[$_[:alpha:]][$_[:alnum:]]*[\\t ]*=[\\t ]*\"\n> +\t\t\"(\\\\(.*\\\\)|[$_[:alpha:]][$_[:alnum:]]*)[\\t ]*=>[\\t ]*\\\\{?)\\n\"\n> +\t /* match object's property assignment by anonymous function and CommonJS exports for named function */\n> +\t \"^((module\\\\.)?[$_[:alpha:]][$_[:alnum:]]*\\\\.[$_[:alpha:]][$_[:alnum:]]*[\\t ]*=[\\t ]*(async[\\t ]+)?(\\\\(.*\\\\)|[$_[:alpha:]][$_[:alnum:]]*)[\\t ]*=>.*)\\n\"\n> +\t /* match assign function to LHS with explicit function keyword */\n> +\t \"^(.*=[\\t ]*function[\\t ]*([$_[:alnum:]]+[\\t ]*)?\\\\(.*)\\n\"\n> +\t /* popular unit testing framework test case pattern. Most of framework pattern is match by regex for \"function in class\" */\n> +\t \"^[\\t ]*(QUnit.test\\\\(.*)\\n\"\n> +\t /* don't match the function in class or in object literal, which has more than one ident level */\n> +\t \"!^(\\t{2,}|[ ]{5,})\\n\"\n> +\t /* match normal function in object literal */\n> +\t \"^[\\t ]*([$_[:alpha:]][$_[:alnum:]]*[\\t ]*:[\\t ]*function.*)\\n\"\n> +\t /* don't match chained method call */\n> +\t \"!^[\\t ]*[$_[:alpha:]][$_[:alnum:]][\\t ]*\\\\(.*\\\\)\\\\.\\n\"\n> +\t /* match function in class and ES5 method shorthand */\n> +\t \"^[\\t ]*((static[\\t ]+)?((async|get|set)[\\t ]+)?[$_[:alpha:]][$_[:alnum:]]*[\\t ]*\\\\(.*)\",\n\nNo objections to these expressions.\n\n> +\t /* word regex */\n> +\t /* hexIntegerLiteral, octalIntegerLiteral, binaryIntegerLiteral, and its big version */\n> +\t \"0[xXoObB][_0-9a-fA-F]+n?\"\n> +\t /* DecimalLiteral and its big version*/\n> +\t \"|[0-9][_0-9]*(\\\\.[0-9][_0-9]*|n)?([eE][+-]?[_0-9]+)?\"\n> +\t \"|\\\\.[0-9][_0-9]*([eE][+-]?[_0-9]+)?\"\n> +\t /* punctuations */\n> +\t \"|\\\\.{3}|<=|>=|==|!=|={3}|!==|\\\\*{2}|\\\\+{2}|--|<<|>>\"\n> +\t \"|>>>|&&|\\\\|{2}|\\\\?{2}|\\\\+=|-=|\\\\*=|%=|\\\\*{2}=\"\n> +\t \"|<<=|>>=|>>>=|&=|\\\\|=|\\\\^=|&&=|\\\\|{2}=|\\\\?{2}=|=>\"\n> +\t /* identifiers */\n> +\t \"|[$_[:alpha:]][$_[:alnum:]]*\"),\n\nThese word-regexs look good.\n\n>  PATTERNS(\"markdown\",\n>  \t \"^ {0,3}#{1,6}[ \\t].*\",\n>  \t /* -- */\n\nThis round looks quite good. With the one issue I noted above fixed,\nthis should be good to go, IMO.\n\n-- Hannes\n"},{"id":"453027","messageId":"220404.86lewljovj.gmgdl@evledraar.gmail.com","threadId":"57508","inReplyTo":"20220403132508.28196-1-a97410985new@gmail.com","subject":"Re: [GSoC][PATCH v3] Add a diff driver for JavaScript languages.","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-04T07:12:41Z","receivedAt":"2022-04-04T07:21:01Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Sun, Apr 03 2022, xing zhi jiang wrote:\n\nAside from what Johannes Sixt mentioned:\n\n> +PATTERNS(\"javascript\",\n> +\t /* don't match the expression may contain parenthesis, because it is not a function declaration */\n> +\t \"!^[ \\t]*(if|do|while|for|with|switch|catch|import|return)\\n\"\n> +\t /* don't match statement */\n> +\t \"!;\\n\"\n> +\t /* match normal function or named export for function in ECMA2015 */\n> +\t \"^((export[\\t ]+)?(async[\\t ]+)?function[\\t ]*[\\t *]*[$_[:alpha:]][$_[:alnum:]]*[\\t ]*\\\\(.*)\\n\"\n> +\t /* match JavaScript variable declaration with a lambda expression at top level */\n> +\t \"^((const|let|var)[\\t ]*[$_[:alpha:]][$_[:alnum:]]*[\\t ]*=[\\t ]*\"\n> +\t\t\"(\\\\(.*\\\\)|[$_[:alpha:]][$_[:alnum:]]*)[\\t ]*=>[\\t ]*\\\\{?)\\n\"\n> +\t /* match object's property assignment by anonymous function and CommonJS exports for named function */\n> +\t \"^((module\\\\.)?[$_[:alpha:]][$_[:alnum:]]*\\\\.[$_[:alpha:]][$_[:alnum:]]*[\\t ]*=[\\t ]*(async[\\t ]+)?(\\\\(.*\\\\)|[$_[:alpha:]][$_[:alnum:]]*)[\\t ]*=>.*)\\n\"\n> +\t /* match assign function to LHS with explicit function keyword */\n> +\t \"^(.*=[\\t ]*function[\\t ]*([$_[:alnum:]]+[\\t ]*)?\\\\(.*)\\n\"\n> +\t /* popular unit testing framework test case pattern. Most of framework pattern is match by regex for \"function in class\" */\n> +\t \"^[\\t ]*(QUnit.test\\\\(.*)\\n\"\n> +\t /* don't match the function in class or in object literal, which has more than one ident level */\n> +\t \"!^(\\t{2,}|[ ]{5,})\\n\"\n> +\t /* match normal function in object literal */\n> +\t \"^[\\t ]*([$_[:alpha:]][$_[:alnum:]]*[\\t ]*:[\\t ]*function.*)\\n\"\n> +\t /* don't match chained method call */\n> +\t \"!^[\\t ]*[$_[:alpha:]][$_[:alnum:]][\\t ]*\\\\(.*\\\\)\\\\.\\n\"\n> +\t /* match function in class and ES5 method shorthand */\n> +\t \"^[\\t ]*((static[\\t ]+)?((async|get|set)[\\t ]+)?[$_[:alpha:]][$_[:alnum:]]*[\\t ]*\\\\(.*)\",\n> +\t /* word regex */\n> +\t /* hexIntegerLiteral, octalIntegerLiteral, binaryIntegerLiteral, and its big version */\n> +\t \"0[xXoObB][_0-9a-fA-F]+n?\"\n> +\t /* DecimalLiteral and its big version*/\n> +\t \"|[0-9][_0-9]*(\\\\.[0-9][_0-9]*|n)?([eE][+-]?[_0-9]+)?\"\n> +\t \"|\\\\.[0-9][_0-9]*([eE][+-]?[_0-9]+)?\"\n> +\t /* punctuations */\n> +\t \"|\\\\.{3}|<=|>=|==|!=|={3}|!==|\\\\*{2}|\\\\+{2}|--|<<|>>\"\n> +\t \"|>>>|&&|\\\\|{2}|\\\\?{2}|\\\\+=|-=|\\\\*=|%=|\\\\*{2}=\"\n> +\t \"|<<=|>>=|>>>=|&=|\\\\|=|\\\\^=|&&=|\\\\|{2}=|\\\\?{2}=|=>\"\n> +\t /* identifiers */\n> +\t \"|[$_[:alpha:]][$_[:alnum:]]*\"),\n>  PATTERNS(\"markdown\",\n>  \t \"^ {0,3}#{1,6}[ \\t].*\",\n>  \t /* -- */<\n\nWhile we don't use helper macros for these currently there's no reason\nwe can't, I thin the above might be more readable with e.g.:\n\n\t#define JS_AA \"[$_[:alpha:]][$_[:alnum:]]\"\n\nWhich would make this:\n\t\n\t+PATTERNS(\"javascript\",\n\t+\t /* don't match the expression may contain parenthesis, because it is not a function declaration */\n\t+\t \"!^[ \\t]*(if|do|while|for|with|switch|catch|import|return)\\n\"\n\t+\t /* don't match statement */\n\t+\t \"!;\\n\"\n\t+\t /* match normal function or named export for function in ECMA2015 */\n\t+\t \"^((export[\\t ]+)?(async[\\t ]+)?function[\\t ]*[\\t *]*\" JS_AA \"*[\\t ]*\\\\(.*)\\n\"\n\t+\t /* match JavaScript variable declaration with a lambda expression at top level */\n\t+\t \"^((const|let|var)[\\t ]*\" JS_AA \"*[\\t ]*=[\\t ]*\"\n\t+\t\t\"(\\\\(.*\\\\)|\" JS_AA \"*)[\\t ]*=>[\\t ]*\\\\{?)\\n\"\n\t+\t /* match object's property assignment by anonymous function and CommonJS exports for named function */\n\t+\t \"^((module\\\\.)?\" JS_AA \"*\\\\.\" JS_AA \"*[\\t ]*=[\\t ]*(async[\\t ]+)?(\\\\(.*\\\\)|\" JS_AA \"*)[\\t ]*=>.*)\\n\"\n\t+\t /* match assign function to LHS with explicit function keyword */\n\t+\t \"^(.*=[\\t ]*function[\\t ]*([$_[:alnum:]]+[\\t ]*)?\\\\(.*)\\n\"\n\t+\t /* popular unit testing framework test case pattern. Most of framework pattern is match by regex for \"function in class\" */\n\nWry try to stick to wrapping at 80 characters, so some of these comments\nshould really be wrapped (see CodingGuidelines for the multi-line\ncomment style we use).\n\n\t+\t \"^[\\t ]*(QUnit.test\\\\(.*)\\n\"\n\t+\t /* don't match the function in class or in object literal, which has more than one ident level */\n\t+\t \"!^(\\t{2,}|[ ]{5,})\\n\"\n\t+\t /* match normal function in object literal */\n\t+\t \"^[\\t ]*(\" JS_AA \"*[\\t ]*:[\\t ]*function.*)\\n\"\n\t+\t /* don't match chained method call */\n\t+\t \"!^[\\t ]*\" JS_AA \"[\\t ]*\\\\(.*\\\\)\\\\.\\n\"\n\t+\t /* match function in class and ES5 method shorthand */\n\t+\t \"^[\\t ]*((static[\\t ]+)?((async|get|set)[\\t ]+)?\" JS_AA \"*[\\t ]*\\\\(.*)\",\n\t+\t /* word regex */\n\t+\t /* hexIntegerLiteral, octalIntegerLiteral, binaryIntegerLiteral, and its big version */\n\t+\t \"0[xXoObB][_0-9a-fA-F]+n?\"\n\t+\t /* DecimalLiteral and its big version*/\n\t+\t \"|[0-9][_0-9]*(\\\\.[0-9][_0-9]*|n)?([eE][+-]?[_0-9]+)?\"\n\t+\t \"|\\\\.[0-9][_0-9]*([eE][+-]?[_0-9]+)?\"\n\t+\t /* punctuations */\n\t+\t \"|\\\\.{3}|<=|>=|==|!=|={3}|!==|\\\\*{2}|\\\\+{2}|--|<<|>>\"\n\t+\t \"|>>>|&&|\\\\|{2}|\\\\?{2}|\\\\+=|-=|\\\\*=|%=|\\\\*{2}=\"\n\t+\t \"|<<=|>>=|>>>=|&=|\\\\|=|\\\\^=|&&=|\\\\|{2}=|\\\\?{2}=|=>\"\n\t+\t /* identifiers */\n\t+\t \"|\" JS_AA \"*\"),\n\t\nJust a thought, I wonder how much line-noisy we could make this thing in\ngeneral if we defined some common patterns with such helpers.\n\nAnyway, insted of :alnum:and :alpha: don't you really mean [a-zA-Z0-9]\nand [a-zA-Z]. I.e. do you *really* want to have this different depending\non the user's locale?\n\nI haven't tested, but see the LC_CTYPE in gettext.c, so I'm fairly sure\nthat'll happen...\n\n"},{"id":"453042","messageId":"kl6ly20kycs5.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"57508","inReplyTo":"20220403132508.28196-1-a97410985new@gmail.com","subject":"Re: [GSoC][PATCH v3] Add a diff driver for JavaScript languages.","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2022-04-04T17:32:58Z","receivedAt":"2022-04-04T21:23:11Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"\nI'm pretty happy to see follow up on this patch, I think it'll be very\nuseful. Thanks! I think this patch has a fairly consistent direction\nnow; though I'm undecided on whether or not I agree with the direction,\nI won't block this from going through.\n\nSo treat these comments as suggestions and additional JS info for the\nfolks who actually matter :)\n\nxing zhi jiang <a97410985new@gmail.com> writes:\n\n> In the xfunction part that matches normal functions,\n> a variable declaration with an assignment of function, the function declaration\n> in the class, named export for function in ECMA2015, CommonJS exports for\n> named function, and also the function is object literal's property[1].\n> The special match is this would match the popular framework test case\n> pattern(most is same as regex for matching function in class).\n\nWith this paragraph and..\n\n> 2. \"^[\\t ]*(QUnit.test\\\\(.*)\\n\"\n\nthis pattern, it looks like the direction we've chosen to take is\n\"support popular libraries, even if this might detect the wrong thing in\nvanilla JS\", which sounds reasonable enough to me. I think the\nexperience would be a bit inconsistent for vanilla JS folks, but the\nworst thing I can imagine is that we are a bit overeager to detect\nheaders.\n\nAs an aside, this is my first time hearing of QUnit, but I guess the JS\nlandscape is huge and changes too often for me to keep track.\n\n>    I oberve there are many test case pattern in JavaScript. And most of popular JavaScript\n>    unit test framework's test case pattern is function call can be matched by the regex, which\n>    match \"function in class\". So it is no problem. But `QUnit framework` don't matched by that.\n>    Then add this regex into JavaScript userdiff.\n>    And the reason includes test case pattern has two\n>    1. This is inevitable. Because we can't distinguish that with function in class.\n>    2. This can help the user understand what change is happening in which test case. And if \n>       don't match the test case, it has the large possibility to match no related function.\n\nNit: since we're adding a pattern just for QUnit, it would be nice to\nhave a test for it. This would also have the nice benefit of showing why\nQUnit can't make use of the other patterns.\n\n> +\t /* popular unit testing framework test case pattern. Most of framework pattern is match by regex for \"function in class\" */\n> +\t \"^[\\t ]*(QUnit.test\\\\(.*)\\n\"\n\nThis wasn't entirely clear to me on its own, but reading your ---\ndescription, I believe you mean that other unit testing frameworks are\ncovered by another regex.\n\nWording suggestion, perhaps:\n\n  /*\n   * QUnit (popular unit testing framework) test case; most of the other\n   * frameworks are matched by \"function in class\".\n   */\n\nAs a technicality, there is no guarantee that the user will import the\nQUnit library as the variable `QUnit`, though most users will probably\nuse `QUnit` out of convention, so I don't think this a problem [1].\n\n[1] In case some additional context becomes useful to other reviewers:\n most JavaScript module systems do not enforce variable names when\n importing a module, so there is no guarantee that QUnit will be\n imported as the token 'QUnit', though this is often true by\n _convention_ because library authors often stick to a single name in\n their documentation.\n \n In 'old school JavaScript in the browser', libraries are often exported\n as single global variables. Those do have well-enforced names, because\n otherwise the globals would be unusable.\n \n In native ES modules (aka standardized, modern JavaScript supported by\n modern browsers and most frameworks), the library author can suggest a\n name using named exports, but with the caveats 1. the importer can\n alias the variable to a different name and 2. many authors use\n anonymous exports - either using a default export or asking users to\n import the entire library as a single, anonymous object.\n \n In CommonJS (typically used in NodeJS), one cannot name exports IIRC;\n an export is always a single, anonymous object.\n"},{"id":"453059","messageId":"660d068f-1c8c-7057-0a92-5100791daf80@kdbg.org","threadId":"57508","inReplyTo":"220404.86lewljovj.gmgdl@evledraar.gmail.com","subject":"Re: [GSoC][PATCH v3] Add a diff driver for JavaScript languages.","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2022-04-04T20:29:30Z","receivedAt":"2022-04-04T21:23:49Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 04.04.22 um 09:12 schrieb Ævar Arnfjörð Bjarmason:\n> While we don't use helper macros for these currently there's no reason\n> we can't, I thin the above might be more readable with e.g.:\n> \n> \t#define JS_AA \"[$_[:alpha:]][$_[:alnum:]]\"\n\nPlease consider including \"identifier\" somehow in the macro name. And\nadd the trailing '*', which...\n\n> Which would make this:\n> \t\n> \t+PATTERNS(\"javascript\",\n> \t+\t /* don't match the expression may contain parenthesis, because it is not a function declaration */\n> \t+\t \"!^[ \\t]*(if|do|while|for|with|switch|catch|import|return)\\n\"\n> \t+\t /* don't match statement */\n> \t+\t \"!;\\n\"\n> \t+\t /* match normal function or named export for function in ECMA2015 */\n> \t+\t \"^((export[\\t ]+)?(async[\\t ]+)?function[\\t ]*[\\t *]*\" JS_AA \"*[\\t ]*\\\\(.*)\\n\"\n> \t+\t /* match JavaScript variable declaration with a lambda expression at top level */\n> \t+\t \"^((const|let|var)[\\t ]*\" JS_AA \"*[\\t ]*=[\\t ]*\"\n> \t+\t\t\"(\\\\(.*\\\\)|\" JS_AA \"*)[\\t ]*=>[\\t ]*\\\\{?)\\n\"\n> \t+\t /* match object's property assignment by anonymous function and CommonJS exports for named function */\n> \t+\t \"^((module\\\\.)?\" JS_AA \"*\\\\.\" JS_AA \"*[\\t ]*=[\\t ]*(async[\\t ]+)?(\\\\(.*\\\\)|\" JS_AA \"*)[\\t ]*=>.*)\\n\"\n> \t+\t /* match assign function to LHS with explicit function keyword */\n> \t+\t \"^(.*=[\\t ]*function[\\t ]*([$_[:alnum:]]+[\\t ]*)?\\\\(.*)\\n\"\n> \t+\t /* popular unit testing framework test case pattern. Most of framework pattern is match by regex for \"function in class\" */\n> \n> Wry try to stick to wrapping at 80 characters, so some of these comments\n> should really be wrapped (see CodingGuidelines for the multi-line\n> comment style we use).\n> \n> \t+\t \"^[\\t ]*(QUnit.test\\\\(.*)\\n\"\n> \t+\t /* don't match the function in class or in object literal, which has more than one ident level */\n> \t+\t \"!^(\\t{2,}|[ ]{5,})\\n\"\n> \t+\t /* match normal function in object literal */\n> \t+\t \"^[\\t ]*(\" JS_AA \"*[\\t ]*:[\\t ]*function.*)\\n\"\n> \t+\t /* don't match chained method call */\n> \t+\t \"!^[\\t ]*\" JS_AA \"[\\t ]*\\\\(.*\\\\)\\\\.\\n\"\n\n... which makes me wonder why it is not present here. If that's an\noversight: nice catch!\n\n> \t+\t /* match function in class and ES5 method shorthand */\n> \t+\t \"^[\\t ]*((static[\\t ]+)?((async|get|set)[\\t ]+)?\" JS_AA \"*[\\t ]*\\\\(.*)\",\n> \t+\t /* word regex */\n> \t+\t /* hexIntegerLiteral, octalIntegerLiteral, binaryIntegerLiteral, and its big version */\n> \t+\t \"0[xXoObB][_0-9a-fA-F]+n?\"\n> \t+\t /* DecimalLiteral and its big version*/\n> \t+\t \"|[0-9][_0-9]*(\\\\.[0-9][_0-9]*|n)?([eE][+-]?[_0-9]+)?\"\n> \t+\t \"|\\\\.[0-9][_0-9]*([eE][+-]?[_0-9]+)?\"\n> \t+\t /* punctuations */\n> \t+\t \"|\\\\.{3}|<=|>=|==|!=|={3}|!==|\\\\*{2}|\\\\+{2}|--|<<|>>\"\n> \t+\t \"|>>>|&&|\\\\|{2}|\\\\?{2}|\\\\+=|-=|\\\\*=|%=|\\\\*{2}=\"\n> \t+\t \"|<<=|>>=|>>>=|&=|\\\\|=|\\\\^=|&&=|\\\\|{2}=|\\\\?{2}=|=>\"\n> \t+\t /* identifiers */\n> \t+\t \"|\" JS_AA \"*\"),\n> \t\n> Just a thought, I wonder how much line-noisy we could make this thing in\n> general if we defined some common patterns with such helpers.\n> \n> Anyway, insted of :alnum:and :alpha: don't you really mean [a-zA-Z0-9]\n> and [a-zA-Z]. I.e. do you *really* want to have this different depending\n> on the user's locale?\n\nThat's worth considering.\n\n> \n> I haven't tested, but see the LC_CTYPE in gettext.c, so I'm fairly sure\n> that'll happen...\n> \n\n-- Hannes\n"},{"id":"453063","messageId":"xmqqee2c34n8.fsf@gitster.g","threadId":"57508","inReplyTo":"660d068f-1c8c-7057-0a92-5100791daf80@kdbg.org","subject":"Re: [GSoC][PATCH v3] Add a diff driver for JavaScript languages.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-04-04T21:44:27Z","receivedAt":"2022-04-04T22:16:56Z","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 04.04.22 um 09:12 schrieb Ævar Arnfjörð Bjarmason:\n>> While we don't use helper macros for these currently there's no reason\n>> we can't, I thin the above might be more readable with e.g.:\n>> \n>> \t#define JS_AA \"[$_[:alpha:]][$_[:alnum:]]\"\n>\n> Please consider including \"identifier\" somehow in the macro name. And\n> add the trailing '*', which...\n>\n> ... which makes me wonder why it is not present here. If that's an\n> oversight: nice catch!\n\nYeah, and taken together with the locale worry, we shouldn't play\ncute and use :alpha: and :alnum:, but spell them out, i.e.\n\n\t#define JS_IDENT \"[$_a-zA-Z][$_a-zA-Z0-9]\"\n\nprobably.\n"},{"id":"453075","messageId":"220405.861qycmfdv.gmgdl@evledraar.gmail.com","threadId":"57508","inReplyTo":"660d068f-1c8c-7057-0a92-5100791daf80@kdbg.org","subject":"Re: [GSoC][PATCH v3] Add a diff driver for JavaScript languages.","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-05T02:22:49Z","receivedAt":"2022-04-05T02:46:47Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, Apr 04 2022, Johannes Sixt wrote:\n\n> Am 04.04.22 um 09:12 schrieb Ævar Arnfjörð Bjarmason:\n>> While we don't use helper macros for these currently there's no reason\n>> we can't, I thin the above might be more readable with e.g.:\n>> \n>> \t#define JS_AA \"[$_[:alpha:]][$_[:alnum:]]\"\n>\n> Please consider including \"identifier\" somehow in the macro name. And\n> add the trailing '*', which...\n\nIndeed, although for something like this a cute short name is probably\nOK, and we can just #undef it right afer.\n\n>> Which would make this:\n>> \t\n>> \t+PATTERNS(\"javascript\",\n>> \t+\t /* don't match the expression may contain parenthesis, because it is not a function declaration */\n>> \t+\t \"!^[ \\t]*(if|do|while|for|with|switch|catch|import|return)\\n\"\n>> \t+\t /* don't match statement */\n>> \t+\t \"!;\\n\"\n>> \t+\t /* match normal function or named export for function in ECMA2015 */\n>> \t+\t \"^((export[\\t ]+)?(async[\\t ]+)?function[\\t ]*[\\t *]*\" JS_AA \"*[\\t ]*\\\\(.*)\\n\"\n>> \t+\t /* match JavaScript variable declaration with a lambda expression at top level */\n>> \t+\t \"^((const|let|var)[\\t ]*\" JS_AA \"*[\\t ]*=[\\t ]*\"\n>> \t+\t\t\"(\\\\(.*\\\\)|\" JS_AA \"*)[\\t ]*=>[\\t ]*\\\\{?)\\n\"\n>> \t+\t /* match object's property assignment by anonymous function and CommonJS exports for named function */\n>> \t+\t \"^((module\\\\.)?\" JS_AA \"*\\\\.\" JS_AA \"*[\\t ]*=[\\t ]*(async[\\t ]+)?(\\\\(.*\\\\)|\" JS_AA \"*)[\\t ]*=>.*)\\n\"\n>> \t+\t /* match assign function to LHS with explicit function keyword */\n>> \t+\t \"^(.*=[\\t ]*function[\\t ]*([$_[:alnum:]]+[\\t ]*)?\\\\(.*)\\n\"\n>> \t+\t /* popular unit testing framework test case pattern. Most of framework pattern is match by regex for \"function in class\" */\n>> \n>> Wry try to stick to wrapping at 80 characters, so some of these comments\n>> should really be wrapped (see CodingGuidelines for the multi-line\n>> comment style we use).\n>> \n>> \t+\t \"^[\\t ]*(QUnit.test\\\\(.*)\\n\"\n>> \t+\t /* don't match the function in class or in object literal, which has more than one ident level */\n>> \t+\t \"!^(\\t{2,}|[ ]{5,})\\n\"\n>> \t+\t /* match normal function in object literal */\n>> \t+\t \"^[\\t ]*(\" JS_AA \"*[\\t ]*:[\\t ]*function.*)\\n\"\n>> \t+\t /* don't match chained method call */\n>> \t+\t \"!^[\\t ]*\" JS_AA \"[\\t ]*\\\\(.*\\\\)\\\\.\\n\"\n>\n> ... which makes me wonder why it is not present here. If that's an\n> oversight: nice catch!\n\n*Nod*, I just did a dumb search replace and didn't notice that myself,\n but it's clearly making things easier to read.\n\nAsanother thing I noticed: shouldn't that '.' in QUnit.test be escaped?\nPresumably we don't want QUnitXtest or whatever.\n\n>> \t+\t /* match function in class and ES5 method shorthand */\n>> \t+\t \"^[\\t ]*((static[\\t ]+)?((async|get|set)[\\t ]+)?\" JS_AA \"*[\\t ]*\\\\(.*)\",\n>> \t+\t /* word regex */\n>> \t+\t /* hexIntegerLiteral, octalIntegerLiteral, binaryIntegerLiteral, and its big version */\n>> \t+\t \"0[xXoObB][_0-9a-fA-F]+n?\"\n>> \t+\t /* DecimalLiteral and its big version*/\n>> \t+\t \"|[0-9][_0-9]*(\\\\.[0-9][_0-9]*|n)?([eE][+-]?[_0-9]+)?\"\n>> \t+\t \"|\\\\.[0-9][_0-9]*([eE][+-]?[_0-9]+)?\"\n>> \t+\t /* punctuations */\n>> \t+\t \"|\\\\.{3}|<=|>=|==|!=|={3}|!==|\\\\*{2}|\\\\+{2}|--|<<|>>\"\n>> \t+\t \"|>>>|&&|\\\\|{2}|\\\\?{2}|\\\\+=|-=|\\\\*=|%=|\\\\*{2}=\"\n>> \t+\t \"|<<=|>>=|>>>=|&=|\\\\|=|\\\\^=|&&=|\\\\|{2}=|\\\\?{2}=|=>\"\n>> \t+\t /* identifiers */\n>> \t+\t \"|\" JS_AA \"*\"),\n>> \t\n>> Just a thought, I wonder how much line-noisy we could make this thing in\n>> general if we defined some common patterns with such helpers.\n>> \n>> Anyway, insted of :alnum:and :alpha: don't you really mean [a-zA-Z0-9]\n>> and [a-zA-Z]. I.e. do you *really* want to have this different depending\n>> on the user's locale?\n>\n> That's worth considering.\n\nIf it's intentional it makes sense to add some locale-stressing tests to\nthe tests, i.e. if we really mean to match non-ASCII identifiers etc.\n\n>> \n>> I haven't tested, but see the LC_CTYPE in gettext.c, so I'm fairly sure\n>> that'll happen...\n>> \n>\n> -- Hannes\n\n"}]}