[gtksourceview: 1/6] javascript.lang: Rewrite using context / language parsing



commit 343aa3a91f64261d679bf338ffefb370d1af5177
Author: Jeffery To <jeffery to gmail com>
Date:   Thu Oct 10 23:00:05 2019 +0800

    javascript.lang: Rewrite using context / language parsing

 data/language-specs/html.lang                      |   14 +-
 data/language-specs/javascript-expressions.lang    |  746 ++++++++++
 .../javascript-functions-classes.lang              |  484 +++++++
 data/language-specs/javascript-literals.lang       |  601 ++++++++
 data/language-specs/javascript-modules.lang        |  435 ++++++
 data/language-specs/javascript-statements.lang     |  923 ++++++++++++
 data/language-specs/javascript-values.lang         |  697 +++++++++
 data/language-specs/javascript.lang                | 1472 +++++++++-----------
 data/language-specs/json.lang                      |    4 +-
 data/styles/classic.xml                            |    3 +-
 data/styles/kate.xml                               |    3 +-
 data/styles/tango.xml                              |    3 +-
 po/POTFILES.skip                                   |    6 +
 tests/syntax-highlighting/file.html                |    6 +
 tests/syntax-highlighting/file.js                  |  848 ++++++++++-
 15 files changed, 5385 insertions(+), 860 deletions(-)
---
diff --git a/data/language-specs/html.lang b/data/language-specs/html.lang
index 30145c2a..5fa31275 100644
--- a/data/language-specs/html.lang
+++ b/data/language-specs/html.lang
@@ -330,13 +330,25 @@
           <include>
             <context sub-pattern="0" where="start" style-ref="tag"/>
             <context sub-pattern="0" where="end" style-ref="tag"/>
-            <context ref="embedded-lang-hook"/>
             <context ref="js:js"/>
           </include>
         </context>
       </include>
     </context>
 
+    <context id="js-embedded-lang-hooks">
+      <include>
+        <context end-parent="true">
+          <start>(?=&lt;/script(?:&gt;|\s|$))</start>
+          <end>\%{def:always-match}</end>
+        </context>
+        <context ref="embedded-lang-hook"/>
+        <context ref="js:embedded-lang-hooks" original="true"/>
+      </include>
+    </context>
+
+    <replace id="js:embedded-lang-hooks" ref="js-embedded-lang-hooks"/>
+
     <context id="tag" class="no-spell-check">
       <start>&lt;/?[a-z0-9_-]+</start>
       <end>/?&gt;</end>
diff --git a/data/language-specs/javascript-expressions.lang b/data/language-specs/javascript-expressions.lang
new file mode 100644
index 00000000..5285196d
--- /dev/null
+++ b/data/language-specs/javascript-expressions.lang
@@ -0,0 +1,746 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ This file is part of GtkSourceView
+
+ Author: Scott Martin <scott coffeeblack org>
+ Copyright (C) 2004 Scott Martin <scott coffeeblack org>
+ Copyright (C) 2005 Stef Walter (formerly Nate Nielsen) <stef memberwebs com>
+ Copyright (C) 2005-2007 Marco Barisione <barisione gmail com>
+ Copyright (C) 2005-2007 Emanuele Aina
+ Copyright (C) 2019 Jeffery To <jeffery to gmail com>
+
+ GtkSourceView is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ GtkSourceView is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this library; if not, see <http://www.gnu.org/licenses/>.
+
+-->
+<language id="js-expr" name="JavaScript Expressions" version="2.0" _section="Script" hidden="true">
+  <keyword-char-class>[a-zA-Z0-9_$]</keyword-char-class>
+
+  <definitions>
+
+    <!--
+         See javascript.lang for general notes, naming conventions, etc.
+    -->
+
+    <!--
+         Expression / context structure:
+
+         left-hand side (lhs) expression:  new Array ()
+                                           === ===== ==
+                                           /     |     \
+                                pre-primary   primary   post-primary
+                                expression   expression  expression
+
+         expression: - obj.count + 1
+                     = ========= ===
+                    /      |       \
+             pre-lhs      lhs       post-lhs
+         expression    expression    expression
+    -->
+
+
+    <!-- # Pre-primary expression -->
+
+    <context id="_pre-primary-expression">
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+
+        <!-- ES2017, cannot be followed by line terminator -->
+        <context id="_async-pre-primary-expression-keyword" style-ref="js:keyword">
+          <match extended="true">
+            \%[ async \%]
+            (?=                                       # preceeds arrow function
+              \%{js:optional-whitespace-or-comments}
+              (?:
+                \%{js:identifier}
+                \%{js:optional-whitespace-or-comments}
+                =&gt; |
+                \(                                      # can this be better?
+              )
+            )
+            (?!                                       # does not preceed "function" (leave to function 
expression)
+              \%{js:optional-whitespace-or-comments}
+              \%[ function \%]
+            )
+          </match>
+        </context> <!-- /_async-pre-primary-expression-keyword -->
+
+        <context id="_new-pre-primary-expression-keyword" style-ref="js:keyword">
+          <match extended="true">
+            \%[ new \%] (?! \%{js:new-target-object-keyword-suffix} )
+          </match>
+        </context> <!-- /_new-pre-primary-expression-keyword -->
+
+      </include>
+    </context> <!-- /_pre-primary-expression -->
+
+    <context id="_ordered-pre-primary-expression" once-only="true">
+      <start>\%{js:before-next-token}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="_pre-primary-expression"/>
+      </include>
+    </context> <!-- /_ordered-pre-primary-expression -->
+
+
+    <!-- # Grouping / arrow function parameters
+
+         (2 + 3) * 4
+         (x, y) => x + y
+         (x, ...rest) => { return rest; }
+    -->
+
+    <!-- doing it this way, instead of using expression-with-comma,
+         will make later augmentation easier -->
+
+    <context id="_grouping-item-content">
+      <include>
+        <context ref="js:ordered-rest-syntax"/>
+        <context ref="expression-without-comma"/>
+      </include>
+    </context> <!-- /_grouping-item-content -->
+
+    <!-- <CoverParenthesizedExpressionAndArrowParameterList> -->
+    <context id="_choice-grouping" style-ref="js:grouping" end-parent="true">
+      <start>\(</start>
+      <end>\)</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:grouping-operator"/>
+        <context sub-pattern="0" where="end" style-ref="js:grouping-operator"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_grouping-content">
+          <include>
+
+            <context id="_grouping-first-item" once-only="true">
+              <start>\%{js:before-next-token}</start>
+              <end>\%{js:before-next-token}</end>
+              <include>
+                <context ref="js:embedded-lang-hooks"/>
+                <context ref="js:comments"/>
+                <context ref="_grouping-item-content"/>
+              </include>
+            </context> <!-- /_grouping-first-item -->
+
+            <context id="_grouping-items">
+              <start>,</start>
+              <end>\%{js:before-next-token}</end>
+              <include>
+                <context ref="js:embedded-lang-hooks"/>
+                <context ref="js:comments"/>
+                <context ref="_grouping-item-content"/>
+              </include>
+            </context> <!-- /_grouping-items -->
+
+          </include>
+        </context> <!-- /_grouping-content -->
+
+      </include>
+    </context> <!-- /_choice-grouping -->
+
+
+    <!-- # Primary expression
+
+         this
+         Array
+         'string'
+         100
+         ( ... )
+         [ 1, 2, 3 ]
+         function () { ... }
+         /regex/
+         `template`
+    -->
+
+    <!-- <PrimaryExpression> -->
+    <context id="_primary-expression" once-only="true">
+      <start>\%{js:before-next-token}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_primary-expression-content">
+          <include>
+            <context ref="js-lit:choice-array-literal"/>
+            <context ref="js-lit:choice-object-literal"/>
+            <context ref="js-lit:choice-regular-expression-literal"/>
+            <context ref="js-lit:choice-template-literal"/>
+            <context ref="js-fn:choice-function-expression"/>
+            <context ref="js-fn:choice-class-expression"/>
+            <context ref="_choice-grouping"/>
+            <context ref="js-lit:choice-number"/>
+            <context ref="js-lit:choice-string"/>
+
+            <context id="_choice-primary-expression-identifier" end-parent="true">
+              <start>(?=\%{js:identifier-start})</start>
+              <end>\%{def:always-match}</end>
+              <include>
+                <!-- no embedded-lang-hooks here -->
+                <!-- no comments here -->
+                <!-- do not extend the context by matching comments or
+                     embedded-lang-hooks, which may lead to multiple identifiers -->
+
+                <context id="_primary-expression-identifier-content">
+                  <include>
+                    <!-- technically these would be choices, but it would be very
+                         difficult to turn large keyword contexts into container
+                         contexts with end-parent="true" -->
+                    <context ref="js-lit:null-value"/>
+                    <context ref="js-lit:boolean"/>
+                    <context ref="js-val:global-values"/>
+                    <context ref="js:identifier"/>
+                  </include>
+                </context> <!-- /_primary-expression-identifier-content -->
+
+              </include>
+            </context> <!-- /_choice-primary-expression-identifier -->
+
+          </include>
+        </context> <!-- /_primary-expression-content -->
+
+      </include>
+    </context> <!-- /_primary-expression -->
+
+    <context id="_ordered-primary-expression" once-only="true">
+      <start>\%{js:before-next-token}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="_primary-expression"/>
+      </include>
+    </context> <!-- /_ordered-primary-expression -->
+
+
+    <!-- # Function call
+         (in an expression, function arguments list after primary
+         value)
+
+         fn()
+         fn(a, b, ...list)
+    -->
+
+    <context id="_function-arguments-content">
+      <include>
+        <context ref="js:ordered-spread-syntax"/>
+        <context ref="expression-without-comma"/>
+      </include>
+    </context> <!-- /_function-arguments-content -->
+
+    <!-- <Arguments> -->
+    <context id="_function-arguments-lists">
+      <start>\(</start>
+      <end>\)</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_function-arguments-list-content">
+          <include>
+
+            <context id="_function-first-argument" once-only="true">
+              <start>\%{js:before-next-token}</start>
+              <end>\%{js:before-next-token}</end>
+              <include>
+                <context ref="js:embedded-lang-hooks"/>
+                <context ref="js:comments"/>
+                <context ref="_function-arguments-content"/>
+              </include>
+            </context> <!-- /_function-first-argument -->
+
+            <context id="_function-arguments">
+              <start>,</start>
+              <end>\%{js:before-next-token}</end>
+              <include>
+                <context ref="js:embedded-lang-hooks"/>
+                <context ref="js:comments"/>
+                <context ref="_function-arguments-content"/>
+              </include>
+            </context> <!-- /_function-arguments -->
+
+          </include>
+        </context> <!-- /_function-arguments-list-content -->
+
+      </include>
+    </context> <!-- /_function-arguments-lists -->
+
+
+    <!-- # Post-primary expression
+
+         obj.property
+         obj['property']
+         fn()
+         tag`template`
+    -->
+
+    <context id="_post-primary-expression">
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+
+        <!-- <MemberExpression> (part of) -->
+        <context id="_dot-property-accessors">
+          <start>\.(?!\.)</start> <!-- avoid matching rest/spread syntax -->
+          <end>\%{js:before-next-token}</end>
+          <include>
+            <context ref="js:embedded-lang-hooks"/>
+            <context ref="js:comments"/>
+
+            <context id="_dot-property-accessor-content">
+              <include>
+                <context ref="js-val:properties-methods"/>
+                <context ref="js:identifier"/>
+              </include>
+            </context> <!-- /_dot-property-accessor-content -->
+
+          </include>
+        </context> <!-- /_dot-property-accessors -->
+
+        <!-- <MemberExpression> (part of) -->
+        <context id="_bracket-property-accessors">
+          <start>\[</start>
+          <end>]</end>
+          <include>
+            <context ref="js:embedded-lang-hooks"/>
+            <context ref="js:comments"/>
+
+            <context id="_bracket-property-accessor-content">
+              <include>
+                <context ref="expression-with-comma"/>
+              </include>
+            </context> <!-- /_bracket-property-accessor-content -->
+
+          </include>
+        </context> <!-- /_bracket-property-accessors -->
+
+        <context ref="_function-arguments-lists"/>
+        <context ref="js-lit:template-literals"/>
+      </include>
+    </context> <!-- /_post-primary-expression -->
+
+    <context id="_ordered-post-primary-expression" once-only="true">
+      <start>\%{js:before-next-token}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="_post-primary-expression"/>
+      </include>
+    </context> <!-- /_ordered-post-primary-expression -->
+
+
+    <!-- # Increment / decrement operators -->
+
+    <!-- shared between pre- and post-lhs expressions -->
+    <context id="_increment-decrement-operators" style-ref="js:increment-decrement-operator">
+      <match>(\+\+|--)</match>
+    </context> <!-- /_increment-decrement-operators -->
+
+
+    <!-- # Pre-LHS expression -->
+
+    <context id="_pre-lhs-expression">
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="_increment-decrement-operators"/>
+
+        <context id="_keyword-unary-operators" style-ref="js:keyword">
+          <keyword>await</keyword> <!-- ES2017 -->
+          <keyword>delete</keyword>
+          <keyword>typeof</keyword>
+          <keyword>void</keyword>
+          <keyword>yield</keyword>
+        </context> <!-- /_keyword-unary-operators -->
+
+        <context ref="js:generator-modifier"/> <!-- for yield* -->
+
+        <context id="_unary-operators" style-ref="js:unary-operator">
+          <match extended="true">
+            (
+              \+ |  # unary plus
+              - |   # unary negation
+              ~ |   # bitwise not
+              !     # logical not
+            )
+          </match>
+        </context> <!-- /_unary-operators -->
+
+      </include>
+    </context> <!-- /_pre-lhs-expression -->
+
+    <context id="_ordered-pre-lhs-expression" once-only="true">
+      <start>\%{js:before-next-token}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="_pre-lhs-expression"/>
+      </include>
+    </context> <!-- /_ordered-pre-lhs-expression -->
+
+
+    <!-- # Left-hand side expression
+
+         this
+         new Array
+         'string'.length
+         fn.apply()
+         list[1]
+         tag`template`
+    -->
+
+    <define-regex id="_expression-start" extended="true">
+      (?= [^\s:;\])}] ) (?! /[/*] )
+    </define-regex> <!-- /_expression-start -->
+
+    <!-- <LeftHandSideExpression> -->
+    <context id="lhs-expression" style-ref="js:expression" once-only="true">
+      <start>\%{_expression-start}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_lhs-expression-content">
+          <include>
+            <context ref="_ordered-pre-primary-expression"/>
+            <context ref="_ordered-primary-expression"/>
+            <context ref="_ordered-post-primary-expression"/>
+          </include>
+        </context> <!-- /_lhs-expression-content -->
+
+      </include>
+    </context> <!-- /lhs-expression -->
+
+
+    <!-- # Ternary operator -->
+
+    <context id="_choice-ternary-operator-missing-true-expression" end-parent="true">
+      <start>(?=:)</start>
+      <end>\%{def:always-match}</end>
+    </context> <!-- /_choice-ternary-operator-missing-true-expression -->
+
+    <!-- ## Without comma -->
+
+    <context id="_ternary-operator-without-comma-false-clause" once-only="true">
+      <start>:</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:ternary-operator"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_ternary-operator-without-comma-false-clause-content">
+          <include>
+            <context ref="expression-without-comma"/>
+          </include>
+        </context> <!-- /_ternary-operator-without-comma-false-clause-content -->
+
+      </include>
+    </context> <!-- /_ternary-operator-without-comma-false-clause -->
+
+    <context id="_choice-ternary-operator-without-comma-true-expression" end-parent="true">
+      <start>\%{js:before-next-token}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_ternary-operator-without-comma-true-expression-content">
+          <include>
+            <context ref="expression-without-comma"/>
+            <context ref="_ternary-operator-without-comma-false-clause"/>
+          </include>
+        </context> <!-- /_ternary-operator-without-comma-true-expression-content -->
+
+      </include>
+    </context> <!-- /_choice-ternary-operator-without-comma-true-expression -->
+
+    <context id="_ternary-operators-without-comma">
+      <start>\?</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:ternary-operator"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_ternary-operator-without-comma-content">
+          <include>
+            <context ref="_choice-ternary-operator-missing-true-expression"/>
+            <context ref="_choice-ternary-operator-without-comma-true-expression"/>
+          </include>
+        </context> <!-- /_ternary-operator-without-comma-content -->
+
+      </include>
+    </context> <!-- /_ternary-operators-without-comma -->
+
+    <!-- ## With comma -->
+
+    <context id="_ternary-operator-with-comma-false-clause" once-only="true">
+      <start>:</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:ternary-operator"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_ternary-operator-with-comma-false-clause-content">
+          <include>
+            <context ref="expression-with-comma"/>
+          </include>
+        </context> <!-- /_ternary-operator-with-comma-false-clause-content -->
+
+      </include>
+    </context> <!-- /_ternary-operator-with-comma-false-clause -->
+
+    <context id="_choice-ternary-operator-with-comma-true-expression" end-parent="true">
+      <start>\%{js:before-next-token}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_ternary-operator-with-comma-true-expression-content">
+          <include>
+            <context ref="expression-with-comma"/>
+            <context ref="_ternary-operator-with-comma-false-clause"/>
+          </include>
+        </context> <!-- /_ternary-operator-with-comma-true-expression-content -->
+
+      </include>
+    </context> <!-- /_choice-ternary-operator-with-comma-true-expression -->
+
+    <context id="_ternary-operators-with-comma">
+      <start>\?</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:ternary-operator"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_ternary-operator-with-comma-content">
+          <include>
+            <context ref="_choice-ternary-operator-missing-true-expression"/>
+            <context ref="_choice-ternary-operator-with-comma-true-expression"/>
+          </include>
+        </context> <!-- /_ternary-operator-with-comma-content -->
+
+      </include>
+    </context> <!-- /_ternary-operators-with-comma -->
+
+
+    <!-- # Binary operators -->
+
+    <define-regex id="_keyword-binary-operator" extended="true">
+      (?: \%[ (?: instanceof | in ) \%] )
+    </define-regex> <!-- /_keyword-binary-operator -->
+
+    <!-- excluding comma operator -->
+    <define-regex id="_binary-operator" extended="true">
+      (?:
+        \*\* =? |                            # expotentiation (assignment) (ES2016)
+        [+/*%-] =? |                         # arithmetic (assignment)
+        [!=]==? |                            # equality
+        &amp;&amp; | \|\| |                  # logical
+        [&amp;|^] =? |                       # bitwise logical (assignment)
+        (?: &lt;&lt; | &gt;&gt;&gt;? ) =? |  # bitwise shift (assignment)
+        [&lt;&gt;]=? |                       # relational
+        =                                    # assignment
+      )
+    </define-regex> <!-- /_binary-operator -->
+
+    <!-- ## Without comma -->
+
+    <context id="_keyword-binary-operators-without-comma">
+      <start>\%{_keyword-binary-operator}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:keyword"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_keyword-binary-operator-without-comma-content">
+          <include>
+            <context ref="expression-without-comma"/>
+          </include>
+        </context> <!-- /_keyword-binary-operator-without-comma-content -->
+
+      </include>
+    </context> <!-- /_keyword-binary-operators-without-comma -->
+
+    <context id="_binary-operators-without-comma">
+      <start>\%{_binary-operator}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:binary-operator"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_binary-operator-without-comma-content">
+          <include>
+            <context ref="expression-without-comma"/>
+          </include>
+        </context> <!-- /_binary-operator-without-comma-content -->
+
+      </include>
+    </context> <!-- /_binary-operators-without-comma -->
+
+    <!-- ## With comma -->
+
+    <context id="_keyword-binary-operators-with-comma">
+      <start>\%{_keyword-binary-operator}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:keyword"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_keyword-binary-operator-with-comma-content">
+          <include>
+            <context ref="expression-with-comma"/>
+          </include>
+        </context> <!-- /_keyword-binary-operator-with-comma-content -->
+
+      </include>
+    </context> <!-- /_keyword-binary-operators-with-comma -->
+
+    <context id="_binary-operators-with-comma">
+      <start>(\%{_binary-operator}|,)</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:binary-operator"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_binary-operator-with-comma-content">
+          <include>
+            <context ref="expression-with-comma"/>
+          </include>
+        </context> <!-- /_binary-operator-with-comma-content -->
+      </include>
+    </context> <!-- /_binary-operators-with-comma -->
+
+
+    <!-- # Post-LHS expression -->
+
+    <!-- ## Without comma -->
+
+    <context id="_post-lhs-expression-without-comma">
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="_increment-decrement-operators"/>
+        <context ref="js-fn:arrow-functions"/>
+        <context ref="_ternary-operators-without-comma"/>
+        <context ref="_keyword-binary-operators-without-comma"/>
+        <context ref="_binary-operators-without-comma"/>
+      </include>
+    </context> <!-- /_post-lhs-expression-without-comma -->
+
+    <context id="_ordered-post-lhs-expression-without-comma" once-only="true">
+      <start>\%{js:before-next-token}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="_post-lhs-expression-without-comma"/>
+      </include>
+    </context> <!-- /_ordered-post-lhs-expression-without-comma -->
+
+    <!-- ## With comma -->
+
+    <context id="_post-lhs-expression-with-comma">
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="_increment-decrement-operators"/>
+        <context ref="js-fn:arrow-functions"/>
+        <context ref="_ternary-operators-with-comma"/>
+        <context ref="_keyword-binary-operators-with-comma"/>
+        <context ref="_binary-operators-with-comma"/>
+      </include>
+    </context> <!-- /_post-lhs-expression-with-comma -->
+
+    <context id="_ordered-post-lhs-expression-with-comma" once-only="true">
+      <start>\%{js:before-next-token}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="_post-lhs-expression-with-comma"/>
+      </include>
+    </context> <!-- /_ordered-post-lhs-expression-with-comma -->
+
+
+    <!-- # Expression
+
+         2 + 3 - 1
+         true ? doThis() : doThat()
+    -->
+
+    <!-- ## Without comma -->
+
+    <context id="_expression-without-comma-content">
+      <include>
+        <context ref="_ordered-pre-lhs-expression"/>
+        <context ref="_lhs-expression-content"/>
+        <context ref="_ordered-post-lhs-expression-without-comma"/>
+      </include>
+    </context> <!-- /_expression-without-comma-content -->
+
+    <!-- <AssignmentExpression> -->
+    <context id="expression-without-comma" style-ref="js:expression" once-only="true">
+      <start>\%{_expression-start}(?!,)</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+        <context ref="_expression-without-comma-content"/>
+      </include>
+    </context> <!-- /expression-without-comma -->
+
+    <!-- <AssignmentExpression> -->
+    <context id="choice-expression-without-comma" style-ref="js:expression" end-parent="true">
+      <start>\%{_expression-start}(?!,)</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+        <context ref="_expression-without-comma-content"/>
+      </include>
+    </context> <!-- /choice-expression-without-comma -->
+
+    <!-- ## With comma -->
+
+    <context id="_expression-with-comma-content">
+      <include>
+        <context ref="_ordered-pre-lhs-expression"/>
+        <context ref="_lhs-expression-content"/>
+        <context ref="_ordered-post-lhs-expression-with-comma"/>
+      </include>
+    </context> <!-- /_expression-with-comma-content -->
+
+    <!-- <Expression> -->
+    <context id="expression-with-comma" style-ref="js:expression" once-only="true">
+      <start>\%{_expression-start}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+        <context ref="_expression-with-comma-content"/>
+      </include>
+    </context> <!-- /expression-with-comma -->
+
+    <!-- <Expression> -->
+    <context id="choice-expression-with-comma" style-ref="js:expression" end-parent="true">
+      <start>\%{_expression-start}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+        <context ref="_expression-with-comma-content"/>
+      </include>
+    </context> <!-- /choice-expression-with-comma -->
+
+  </definitions>
+</language>
diff --git a/data/language-specs/javascript-functions-classes.lang 
b/data/language-specs/javascript-functions-classes.lang
new file mode 100644
index 00000000..ad400bab
--- /dev/null
+++ b/data/language-specs/javascript-functions-classes.lang
@@ -0,0 +1,484 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ This file is part of GtkSourceView
+
+ Author: Scott Martin <scott coffeeblack org>
+ Copyright (C) 2004 Scott Martin <scott coffeeblack org>
+ Copyright (C) 2005 Stef Walter (formerly Nate Nielsen) <stef memberwebs com>
+ Copyright (C) 2005-2007 Marco Barisione <barisione gmail com>
+ Copyright (C) 2005-2007 Emanuele Aina
+ Copyright (C) 2019 Jeffery To <jeffery to gmail com>
+
+ GtkSourceView is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ GtkSourceView is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this library; if not, see <http://www.gnu.org/licenses/>.
+
+-->
+<language id="js-fn" name="JavaScript Functions and Classes" version="2.0" _section="Script" hidden="true">
+  <keyword-char-class>[a-zA-Z0-9_$]</keyword-char-class>
+
+  <definitions>
+
+    <!--
+         See javascript.lang for general notes, naming conventions, etc.
+    -->
+
+
+    <!-- # Function expression
+
+         function () { return; }
+         function fn([x, y], z = 3, ...rest) { return x + y + z; }
+    -->
+
+    <!-- ## Function expression keyword -->
+
+    <context id="_function-expression-async-keyword" style-ref="js:keyword">
+      <keyword>async</keyword>
+    </context> <!-- /_function-expression-async-keyword -->
+
+    <context id="_ordered-function-expression-async-keyword" once-only="true">
+      <start>\%{js:before-next-token}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="_function-expression-async-keyword"/>
+      </include>
+    </context> <!-- /_ordered-function-expression-async-keyword -->
+
+    <context id="_function-expression-function-keyword" style-ref="js:keyword">
+      <keyword>function</keyword>
+    </context> <!-- /_function-expression-function-keyword -->
+
+    <context id="_ordered-function-expression-function-keyword" once-only="true">
+      <start>\%{js:before-next-token}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="_function-expression-function-keyword"/>
+      </include>
+    </context> <!-- /_ordered-function-expression-function-keyword -->
+
+    <!-- ## Function parameters list -->
+
+    <context id="_function-parameters-content">
+      <include>
+        <context ref="js:ordered-rest-syntax"/>
+        <context ref="js:ordered-assignment-target"/>
+        <context ref="js:ordered-default-value-assignment"/>
+      </include>
+    </context> <!-- /_function-parameters-content -->
+
+    <context id="_function-parameters-list" once-only="true">
+      <start>\(</start>
+      <end>\)</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_function-parameters-list-content">
+          <include>
+
+            <context id="_function-first-parameter" once-only="true">
+              <start>\%{js:before-next-token}</start>
+              <end>\%{js:before-next-token}</end>
+              <include>
+                <context ref="js:embedded-lang-hooks"/>
+                <context ref="js:comments"/>
+                <context ref="_function-parameters-content"/>
+              </include>
+            </context> <!-- /_function-first-parameter -->
+
+            <context id="_function-parameters">
+              <start>,</start>
+              <end>\%{js:before-next-token}</end>
+              <include>
+                <context ref="js:embedded-lang-hooks"/>
+                <context ref="js:comments"/>
+                <context ref="_function-parameters-content"/>
+              </include>
+            </context> <!-- /_function-parameters -->
+
+          </include>
+        </context> <!-- /_function-parameters-list-content -->
+
+      </include>
+    </context> <!-- /_function-parameters-list -->
+
+    <context id="_ordered-function-parameters-list" once-only="true">
+      <start>\%{js:before-next-token}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="_function-parameters-list"/>
+      </include>
+    </context> <!-- /_ordered-function-parameters-list -->
+
+    <!-- ## Function body -->
+
+    <!-- <FunctionBody> -->
+    <context id="_choice-function-body" end-parent="true">
+      <start>{</start>
+      <end>}</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_function-body-content">
+          <include>
+            <context ref="js-st:directives"/>
+            <context ref="js-st:statements"/>
+          </include>
+        </context> <!-- /_function-body-content -->
+
+      </include>
+    </context> <!-- /_choice-function-body -->
+
+    <context id="_last-function-body" end-parent="true">
+      <start>\%{js:before-next-token}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="_choice-function-body"/>
+      </include>
+    </context> <!-- /_last-function-body -->
+
+    <!-- ## Function expression -->
+
+    <!-- <FunctionExpression> / <FunctionDeclaration> -->
+    <context id="choice-function-expression" style-ref="js:function-expression" end-parent="true">
+      <start>(?=\%{js:function-expression-keyword})</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_function-expression-content">
+          <include>
+            <context ref="_ordered-function-expression-async-keyword"/>
+            <context ref="_ordered-function-expression-function-keyword"/>
+            <context ref="js:ordered-generator-modifier"/>
+            <context ref="js:ordered-identifier"/>
+            <context ref="_ordered-function-parameters-list"/>
+            <context ref="_last-function-body"/>
+          </include>
+        </context> <!-- /_function-expression-content -->
+
+      </include>
+    </context> <!-- /choice-function-expression -->
+
+
+    <!-- # Arrow function
+
+         x => -x;
+         (x, y) => x + y
+         ({ a: x, b: y = 2}) => { return x + y; }
+    -->
+
+    <context id="_arrow-function-body" once-only="true">
+      <start>\%{js:before-next-token}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_arrow-function-body-content">
+          <include>
+            <context ref="_choice-function-body"/>
+            <context ref="js-expr:choice-expression-without-comma"/>
+          </include>
+        </context> <!-- /_arrow-function-body-content -->
+
+      </include>
+    </context> <!-- /_arrow-function-body -->
+
+    <!-- <ArrowFunction> -->
+    <!-- parenthesized arrow function parameters matched by grouping
+         context -->
+    <context id="arrow-functions" style-ref="js:function-expression">
+      <start>=&gt;</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_arrow-function-content">
+          <include>
+            <context ref="_arrow-function-body"/>
+          </include>
+        </context> <!-- /_arrow-function-content -->
+
+      </include>
+    </context> <!-- /arrow-functions -->
+
+
+    <!-- # Method definition
+
+         {
+           method() { ... },
+           get prop() { return this._prop; },
+           set prop(v) { this._prop = v; }
+         }
+    -->
+
+    <context id="_method-definition-modifier" once-only="true">
+      <start>\%{js:before-next-token}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_method-definition-modifier-content">
+          <include>
+
+            <context id="_choice-method-definition-modifier-keyword" style-ref="js:keyword" 
end-parent="true">
+              <keyword>get</keyword>
+              <keyword>set</keyword>
+            </context> <!-- /_choice-method-definition-modifier-keyword -->
+
+            <!-- ES2017, cannot be followed by line terminator -->
+            <context id="_choice-async-method-definition-modifier-keyword" style-ref="js:keyword" 
end-parent="true">
+              <match extended="true">
+                \%[ async \%]
+                (?=
+                  \%{js:optional-whitespace-or-comments}
+                  (?:
+                    \%{js:identifier-start} |
+                    \%{js:generator-modifier}
+                  )
+                )
+              </match>
+            </context> <!-- /_choice-async-method-definition-modifier-keyword -->
+
+          </include>
+        </context> <!-- /_method-definition-modifier-content -->
+
+      </include>
+    </context> <!-- /_method-definition-modifier -->
+
+    <context id="_ordered-method-definition-modifier" once-only="true">
+      <start>\%{js:before-next-token}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="_method-definition-modifier"/>
+      </include>
+    </context> <!-- /_ordered-method-definition-modifier -->
+
+    <!-- <MethodDefinition> -->
+    <context id="_method-definition" once-only="true">
+      <start>\%{js:before-next-token}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_method-definition-content">
+          <include>
+            <context ref="_ordered-method-definition-modifier"/>
+            <context ref="js:ordered-generator-modifier"/>
+            <context ref="js-lit:ordered-property-name"/>
+            <context ref="_ordered-function-parameters-list" style-ref="js:function-expression"/>
+            <context ref="_last-function-body" style-ref="js:function-expression"/>
+          </include>
+        </context> <!-- /_method-definition-content -->
+
+      </include>
+    </context> <!-- /_method-definition -->
+
+    <context id="ordered-method-definition" once-only="true">
+      <start>\%{js:before-next-token}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="_method-definition"/>
+      </include>
+    </context> <!-- /ordered-method-definition -->
+
+
+    <!-- # Class expression
+
+         class Foo extends Bar {
+           constructor() { ... }
+           methodA() { ... }
+           static methodB() { ... }
+         }
+    -->
+
+    <!-- ## Class optional name -->
+
+    <context id="_class-optional-name" once-only="true">
+      <start>\%{js:before-next-token}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_class-optional-name-content">
+          <include>
+
+            <context id="_choice-class-optional-name-ignore-extends" end-parent="true">
+              <start>(?=\%[extends\%])</start>
+              <end>\%{def:always-match}</end>
+            </context> <!-- /_choice-class-optional-name-ignore-extends -->
+
+            <context ref="js:choice-identifier"/>
+          </include>
+        </context> <!-- /_class-optional-name-content -->
+
+      </include>
+    </context> <!-- /_class-optional-name -->
+
+    <context id="_ordered-class-optional-name" once-only="true">
+      <start>\%{js:before-next-token}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="_class-optional-name"/>
+      </include>
+    </context> <!-- /_ordered-class-optional-name -->
+
+    <!-- ## Class extends clause -->
+
+    <context id="_class-extends-clause" once-only="true">
+      <start>\%[extends\%]</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:keyword"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_class-extends-clause-content">
+          <include>
+            <context ref="js-expr:lhs-expression"/>
+          </include>
+        </context> <!-- /_class-extends-clause-content -->
+
+      </include>
+    </context> <!-- /_class-extends-clause -->
+
+    <context id="_ordered-class-extends-clause" once-only="true">
+      <start>\%{js:before-next-token}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="_class-extends-clause"/>
+      </include>
+    </context> <!-- /_ordered-class-extends-clause -->
+
+    <!-- ## Class body -->
+
+    <context id="_class-body-member-modifier" once-only="true">
+      <start>\%{js:before-next-token}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_class-body-member-modifier-content">
+          <include>
+
+            <context id="_choice-class-body-member-modifier-keyword" style-ref="js:keyword" 
end-parent="true">
+              <keyword>static</keyword>
+            </context> <!-- /_choice-class-body-member-modifier-keyword -->
+
+          </include>
+        </context> <!-- /_class-body-member-modifier-content -->
+
+      </include>
+    </context> <!-- /_class-body-member-modifier -->
+
+    <context id="_ordered-class-body-member-modifier" once-only="true">
+      <start>\%{js:before-next-token}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="_class-body-member-modifier"/>
+      </include>
+    </context> <!-- /_ordered-class-body-member-modifier -->
+
+    <context id="_class-body-members">
+      <start>\%{js:before-next-token}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_class-body-member-content">
+          <include>
+            <context ref="_ordered-class-body-member-modifier"/>
+            <context ref="ordered-method-definition"/>
+          </include>
+        </context> <!-- /_class-body-member-content -->
+
+      </include>
+    </context> <!-- /_class-body-members -->
+
+    <context id="_class-body" once-only="true">
+      <start>{</start>
+      <end>}</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_class-body-content">
+          <include>
+            <context ref="_class-body-members"/>
+          </include>
+        </context> <!-- /_class-body-content -->
+
+      </include>
+    </context> <!-- /_class-body -->
+
+    <context id="_last-class-body" end-parent="true">
+      <start>\%{js:before-next-token}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="_class-body"/>
+      </include>
+    </context> <!-- /_last-class-body -->
+
+    <!-- ## Class expression -->
+
+    <!-- <ClassExpression> / <ClassDeclaration> -->
+    <context id="choice-class-expression" style-ref="js:class-expression" end-parent="true">
+      <start>\%{js:class-expression-keyword}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:keyword"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_class-expression-content">
+          <include>
+            <context ref="_ordered-class-optional-name"/>
+            <context ref="_ordered-class-extends-clause"/>
+            <context ref="_last-class-body"/>
+          </include>
+        </context> <!-- /_class-expression-content -->
+
+      </include>
+    </context> <!-- /choice-class-expression -->
+
+    <!-- <ClassExpression> / <ClassDeclaration> -->
+    <context id="choice-class-expression-required-name" style-ref="js:class-expression" end-parent="true">
+      <start>\%{js:class-expression-keyword}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:keyword"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_class-expression-required-name-content">
+          <include>
+            <context ref="js:ordered-identifier"/>
+            <context ref="_ordered-class-extends-clause"/>
+            <context ref="_last-class-body"/>
+          </include>
+        </context> <!-- /_class-expression-required-name-content -->
+
+      </include>
+    </context> <!-- /choice-class-expression-required-name -->
+
+  </definitions>
+</language>
diff --git a/data/language-specs/javascript-literals.lang b/data/language-specs/javascript-literals.lang
new file mode 100644
index 00000000..cdc4bd52
--- /dev/null
+++ b/data/language-specs/javascript-literals.lang
@@ -0,0 +1,601 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ This file is part of GtkSourceView
+
+ Author: Scott Martin <scott coffeeblack org>
+ Copyright (C) 2004 Scott Martin <scott coffeeblack org>
+ Copyright (C) 2005 Stef Walter (formerly Nate Nielsen) <stef memberwebs com>
+ Copyright (C) 2005-2007 Marco Barisione <barisione gmail com>
+ Copyright (C) 2005-2007 Emanuele Aina
+ Copyright (C) 2019 Jeffery To <jeffery to gmail com>
+
+ GtkSourceView is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ GtkSourceView is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this library; if not, see <http://www.gnu.org/licenses/>.
+
+-->
+<language id="js-lit" name="JavaScript Literals" version="2.0" _section="Script" hidden="true">
+  <keyword-char-class>[a-zA-Z0-9_$]</keyword-char-class>
+
+  <definitions>
+
+    <!--
+         See javascript.lang for general notes, naming conventions, etc.
+    -->
+
+
+    <!-- # Keyword values -->
+
+    <!-- <NullLiteral> -->
+    <context id="null-value" style-ref="js:null-value">
+      <keyword>null</keyword>
+    </context> <!-- /null-value -->
+
+    <!-- <BooleanLiteral> -->
+    <context id="boolean" style-ref="js:boolean">
+      <keyword>false</keyword>
+      <keyword>true</keyword>
+    </context> <!-- /boolean -->
+
+
+    <!-- # Number -->
+
+    <!-- <NumericLiteral> -->
+    <context id="choice-number">
+      <include>
+
+        <define-regex id="_decimal" extended="true">
+          (?&gt;
+            (?: [1-9][0-9]* | 0 ) (?: \. [0-9]* )? |
+            \. [0-9]+
+          )
+          (?&gt; [eE] [+-]? [0-9]+ )?
+        </define-regex> <!-- /_decimal -->
+
+        <!-- <DecimalLiteral> -->
+        <context id="_choice-decimal" style-ref="js:decimal" end-parent="true">
+          <start extended="true">
+            (?&lt;! \%{js:identifier-char} | \. )
+            (?=
+              \%{_decimal}
+              (?! \%{js:identifier-part} | \. )
+            )
+          </start>
+          <end>\%{_decimal}</end>
+        </context> <!-- /_choice-decimal -->
+
+        <define-regex id="_decimal-integer" extended="true">
+          (?&gt;
+            (?: [1-9][0-9]* | 0 )
+          )
+        </define-regex> <!-- /_decimal-integer -->
+
+        <!-- ES2020 -->
+        <!-- <DecimalBigIntegerLiteral> -->
+        <context id="_choice-decimal-big-integer" style-ref="js:decimal" end-parent="true">
+          <start extended="true">
+            (?&lt;! \%{js:identifier-char} | \. )
+            (?=
+              \%{_decimal-integer} n
+              (?! \%{js:identifier-part} | \. )
+            )
+          </start>
+          <end>\%{_decimal-integer}n</end>
+        </context> <!-- /_choice-decimal-big-integer -->
+
+        <define-regex id="_binary-integer" extended="true">
+          (?&gt; 0 [bB] [01]+ )
+        </define-regex> <!-- /_binary-integer -->
+
+        <!-- <BinaryIntegerLiteral> -->
+        <context id="_choice-binary-integer" style-ref="js:binary-integer" end-parent="true">
+          <start extended="true">
+            (?&lt;! \%{js:identifier-char} | \. )
+            (?=
+              \%{_binary-integer} n?
+              (?! \%{js:identifier-part} | \. )
+            )
+          </start>
+          <end>\%{_binary-integer}n?</end>
+        </context> <!-- /_choice-binary-integer -->
+
+        <define-regex id="_octal-integer" extended="true">
+          (?&gt; 0 [oO] [0-7]+ )
+        </define-regex> <!-- /_octal-integer -->
+
+        <!-- <OctalIntegerLiteral> -->
+        <context id="_choice-octal-integer" style-ref="js:octal-integer" end-parent="true">
+          <start extended="true">
+            (?&lt;! \%{js:identifier-char} | \. )
+            (?=
+              \%{_octal-integer} n?
+              (?! \%{js:identifier-part} | \. )
+            )
+          </start>
+          <end>\%{_octal-integer}n?</end>
+        </context> <!-- /_choice-octal-integer -->
+
+        <!-- <HexIntegerLiteral> -->
+        <define-regex id="_hex-integer" extended="true">
+          (?&gt; 0 [xX] [0-9a-fA-F]+ )
+        </define-regex> <!-- /_hex-integer -->
+
+        <context id="_choice-hex-integer" style-ref="js:hex-integer" end-parent="true">
+          <start extended="true">
+            (?&lt;! \%{js:identifier-char} | \. )
+            (?=
+              \%{_hex-integer} n?
+              (?! \%{js:identifier-part} | \. )
+            )
+          </start>
+          <end>\%{_hex-integer}n?</end>
+        </context> <!-- /_choice-hex-integer -->
+
+        <define-regex id="_legacy-octal-integer" extended="true">
+          (?&gt; 0 [0-7]+ )
+        </define-regex> <!-- /_legacy-octal-integer -->
+
+        <!-- Annex B: <LegacyOctalIntegerLiteral> -->
+        <context id="_choice-legacy-octal-integer" style-ref="js:error" end-parent="true">
+          <start extended="true">
+            (?&lt;! \%{js:identifier-char} | \. )
+            (?=
+              \%{_legacy-octal-integer}
+              (?! \%{js:identifier-part} | \. )
+            )
+          </start>
+          <end>\%{_legacy-octal-integer}</end>
+        </context> <!-- /_choice-legacy-octal-integer -->
+
+      </include>
+    </context> <!-- /choice-number -->
+
+
+    <!-- # Escape sequences -->
+
+    <context id="_escapes">
+      <include>
+
+        <!-- Annex B: <LegacyOctalEscapeSequence> (preceeded by
+             backslash) -->
+        <context id="_legacy-octal-escape-sequences" style-ref="js:escape">
+          <match extended="true">
+            \\
+            (
+              0     [0-7]{1,2} |  # 0-padded number, not \0 (null character)
+              [1-3] [0-7]{0,2} |  # max \377
+              [4-7] [0-7]{0,1}
+            )
+          </match>
+        </context> <!-- /_legacy-octal-escape-sequences -->
+
+        <!-- <EscapeSequence> (preceeded by backslash) -->
+        <context id="_escape-sequences" style-ref="js:escape">
+          <match extended="true">
+            (
+              \%{js:unicode-escape} |
+              \\
+              (
+                x[0-9a-fA-F]{2} |  # hexadecimal escape
+                [^1-9xu]           # single character escape
+              )
+            )
+          </match>
+        </context> <!-- /_escape-sequences -->
+
+      </include>
+    </context> <!-- /_escapes -->
+
+
+    <!-- # String -->
+
+    <context id="_string-content">
+      <include>
+        <context ref="_escapes"/>
+        <context ref="def:line-continue"/>
+      </include>
+    </context> <!-- /_string-content -->
+
+    <!-- <StringLiteral> -->
+    <context id="choice-string" style-ref="js:string" end-at-line-end="true" end-parent="true" 
class="string" class-disabled="no-spell-check">
+      <start>["']</start>
+      <end>\%{0@start}</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <!-- no comments here -->
+        <context ref="_string-content"/>
+      </include>
+    </context> <!-- /choice-string -->
+
+    <!-- <StringLiteral> -->
+    <context id="choice-string-path" style-ref="js:included-file" end-at-line-end="true" end-parent="true" 
class="path">
+      <start>["']</start>
+      <end>\%{0@start}</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <!-- no comments here -->
+        <context ref="_string-content"/>
+      </include>
+    </context> <!-- /choice-string-path -->
+
+
+    <!-- # Array literal
+
+         [ 1, 2, 3, ...anotherArray ]
+    -->
+
+    <context id="_array-literal-element-content">
+      <include>
+        <context ref="js:ordered-spread-syntax"/>
+        <context ref="js-expr:expression-without-comma"/>
+      </include>
+    </context> <!-- /_array-literal-element-content -->
+
+    <!-- <ArrayLiteral> -->
+    <context id="choice-array-literal" style-ref="js:array-literal" end-parent="true">
+      <start>\[</start>
+      <end>]</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_array-literal-content">
+          <include>
+
+            <context id="_array-literal-first-element" once-only="true">
+              <start>\%{js:before-next-token}</start>
+              <end>\%{js:before-next-token}</end>
+              <include>
+                <context ref="js:embedded-lang-hooks"/>
+                <context ref="js:comments"/>
+                <context ref="_array-literal-element-content"/>
+              </include>
+            </context> <!-- /_array-literal-first-element -->
+
+            <context id="_array-literal-elements">
+              <start>,</start>
+              <end>\%{js:before-next-token}</end>
+              <include>
+                <context ref="js:embedded-lang-hooks"/>
+                <context ref="js:comments"/>
+                <context ref="_array-literal-element-content"/>
+              </include>
+            </context> <!-- /_array-literal-elements -->
+
+          </include>
+        </context> <!-- /_array-literal-content -->
+
+      </include>
+    </context> <!-- /choice-array-literal -->
+
+
+    <!-- # Property name -->
+
+    <!-- <PropertyName> -->
+    <context id="_property-name" once-only="true">
+      <start>\%{js:before-next-token}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_property-name-content">
+          <include>
+
+            <!-- <ComputedPropertyName> -->
+            <context id="_choice-computed-property-name" end-parent="true">
+              <start>\[</start>
+              <end>]</end>
+              <include>
+                <context ref="js:embedded-lang-hooks"/>
+                <context ref="js:comments"/>
+
+                <context id="_computed-property-name-content">
+                  <include>
+                    <context ref="js-expr:expression-without-comma"/>
+                  </include>
+                </context> <!-- /_computed-property-name-content -->
+
+              </include>
+            </context> <!-- /_choice-computed-property-name -->
+
+            <context ref="choice-number"/>
+            <context ref="choice-string"/>
+
+            <define-regex id="_special-property-names" extended="true">
+              (?: \%[ constructor \%] )
+            </define-regex> <!-- /_special-property-names -->
+
+            <context id="_choice-special-property-name" style-ref="js:built-in-method" end-parent="true">
+              <start>(?=\%{_special-property-names})</start>
+              <end>\%{_special-property-names}</end>
+            </context> <!-- /_choice-special-property-name -->
+
+            <context ref="js:choice-identifier-name"/>
+          </include>
+        </context> <!-- /_property-name-content -->
+
+      </include>
+    </context> <!-- /_property-name -->
+
+    <context id="ordered-property-name" once-only="true">
+      <start>\%{js:before-next-token}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="_property-name"/>
+      </include>
+    </context> <!-- /ordered-property-name -->
+
+
+    <!-- # Object literal
+
+         {
+           propertyA: 'a',
+           propertyB: getB(),
+           [ computedName() ]: 2 + 3,
+           method() { ... },
+           get prop() { return this._prop; },
+           set prop(v) { this._prop = v; },
+           ...objectToCopy
+         }
+    -->
+
+    <context id="_object-literal-property-content">
+      <include>
+
+        <context id="_object-literal-property-name" once-only="true">
+          <start>\%{js:before-next-token}</start>
+          <end>\%{js:before-next-token}</end>
+          <include>
+            <context ref="js:embedded-lang-hooks"/>
+            <context ref="js:comments"/>
+
+            <context id="_object-literal-property-name-content">
+              <include>
+                <context ref="js:ordered-spread-syntax"/> <!-- ES2018 -->
+                <context ref="js-fn:ordered-method-definition"/> <!-- includes property-name -->
+              </include>
+            </context> <!-- /_object-literal-property-name-content -->
+
+          </include>
+        </context> <!-- /_object-literal-property-name -->
+
+        <context id="_object-literal-property-value" once-only="true">
+          <start>:</start>
+          <end>\%{js:before-next-token}</end>
+          <include>
+            <context ref="js:embedded-lang-hooks"/>
+            <context ref="js:comments"/>
+
+            <context id="_object-literal-property-value-content">
+              <include>
+                <context ref="js-expr:expression-without-comma"/>
+              </include>
+            </context> <!-- /_object-literal-property-value-content -->
+
+          </include>
+        </context> <!-- /_object-literal-property-value -->
+
+      </include>
+    </context> <!-- /_object-literal-property-content -->
+
+    <!-- <ObjectLiteral> -->
+    <context id="choice-object-literal" style-ref="js:object-literal" end-parent="true">
+      <start>{</start>
+      <end>}</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_object-literal-content">
+          <include>
+
+            <context id="_object-literal-first-property" once-only="true">
+              <start>\%{js:before-next-token}</start>
+              <end>\%{js:before-next-token}</end>
+              <include>
+                <context ref="js:embedded-lang-hooks"/>
+                <context ref="js:comments"/>
+                <context ref="_object-literal-property-content"/>
+              </include>
+            </context> <!-- /_object-literal-first-property -->
+
+            <context id="_object-literal-properties">
+              <start>,</start>
+              <end>\%{js:before-next-token}</end>
+              <include>
+                <context ref="js:embedded-lang-hooks"/>
+                <context ref="js:comments"/>
+                <context ref="_object-literal-property-content"/>
+              </include>
+            </context> <!-- /_object-literal-properties -->
+
+          </include>
+        </context> <!-- /_object-literal-content -->
+
+      </include>
+    </context> <!-- /choice-object-literal -->
+
+
+    <!-- # Regular expression literal
+
+         /(foo|bar)+/u
+    -->
+
+    <!-- only valid in regular expressions -->
+    <context id="_control-escapes" style-ref="js:escape">
+      <match>\\c[a-zA-Z]</match>
+    </context> <!-- /_control-escapes -->
+
+    <!-- ES2018 -->
+    <context id="_unicode-property-escapes" style-ref="js:escape">
+      <match extended="true">
+        \\ [pP] \{ ( [a-zA-Z_]+ = )? [a-zA-Z0-9_]+ \}
+      </match>
+    </context> <!-- /_unicode-property-escapes -->
+
+    <!-- ES2018 -->
+    <context id="_named-capture-groups">
+      <match extended="true">
+        \(\? &lt; ( \%{js:identifier} ) &gt;
+      </match>
+      <include>
+        <context sub-pattern="1" style-ref="js:regex-group"/>
+      </include>
+    </context> <!-- /_named-capture-groups -->
+
+    <!-- ES2018 -->
+    <context id="_named-capture-group-backreferences" style-ref="js:escape">
+      <match extended="true">
+        \\k &lt; ( \%{js:identifier} ) &gt;
+      </match>
+      <include>
+        <context sub-pattern="1" style-ref="js:regex-group"/>
+      </include>
+    </context> <!-- /_named-capture-group-backreferences -->
+
+    <context id="_regular-expression-character-classes" style-ref="js:regex-class" style-inside="true">
+      <start>\[</start>
+      <end>]</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <!-- no comments here -->
+
+        <context id="_regular-expression-character-class-content">
+          <include>
+            <context ref="_control-escapes"/>
+            <context ref="_unicode-property-escapes"/>
+            <context ref="_escapes"/>
+          </include>
+        </context> <!-- /_regular-expression-character-class-content -->
+
+      </include>
+    </context> <!-- /_regular-expression-character-classes -->
+
+    <!-- <RegularExpressionLiteral> -->
+    <!-- technically, line terminators are not allowed inside -->
+    <context id="choice-regular-expression-literal" style-ref="js:regex" end-parent="true">
+      <start>/</start>
+      <end>/([gimsuy]*)</end> <!-- s (dotAll): ES2018 -->
+      <include>
+        <context sub-pattern="1" where="end" style-ref="js:regex-flag"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <!-- no comments here -->
+
+        <context id="_regular-expression-literal-content">
+          <include>
+            <context ref="_control-escapes"/>
+            <context ref="_unicode-property-escapes"/>
+            <context ref="_named-capture-group-backreferences"/>
+            <context ref="_escapes"/>
+            <context ref="_named-capture-groups"/>
+            <context ref="_regular-expression-character-classes"/>
+          </include>
+        </context> <!-- /_regular-expression-literal-content -->
+
+      </include>
+    </context> <!-- /choice-regular-expression-literal -->
+
+
+    <!-- # Template literal (template string)
+
+         `this is a ${adjective} template`
+
+         the contexts are structured this way so that the
+         template-literal and template-placeholder styles (and any
+         styles inside the placeholder) do not overlap
+    -->
+
+    <context id="_template-content">
+      <include>
+        <context ref="_escapes"/>
+        <context ref="def:line-continue"/>
+      </include>
+    </context> <!-- /_template-content -->
+
+    <context id="_template-literal-content">
+      <include>
+
+        <context id="_template-head" style-ref="js:template-literal" once-only="true">
+          <start>(?&lt;=`)</start>
+          <end>(?=`|\$\{)</end>
+          <include>
+            <context ref="js:embedded-lang-hooks"/>
+            <!-- no comments here -->
+            <context ref="_template-content"/>
+          </include>
+        </context> <!-- /_template-head -->
+
+        <context id="_template-substitution-tails" style-ref="js:template-literal">
+          <start>(?&lt;=})</start>
+          <end>(?=`|\$\{)</end>
+          <include>
+            <context ref="js:embedded-lang-hooks"/>
+            <!-- no comments here -->
+            <context ref="_template-content"/>
+          </include>
+        </context> <!-- /_template-substitution-tails -->
+
+        <context id="_template-placeholders">
+          <start>\$\{</start>
+          <end>}</end>
+          <include>
+            <context sub-pattern="0" where="start" style-ref="js:template-placeholder"/>
+            <context sub-pattern="0" where="end" style-ref="js:template-placeholder"/>
+            <context ref="js:embedded-lang-hooks"/>
+            <context ref="js:comments"/>
+
+            <context id="_template-placeholder-content">
+              <include>
+                <context ref="js-expr:expression-with-comma"/>
+              </include>
+            </context> <!-- /_template-placeholder-content -->
+
+          </include>
+        </context> <!-- /_template-placeholders -->
+
+      </include>
+    </context> <!-- /_template-literal-content -->
+
+    <!-- <Template> / <TemplateLiteral> -->
+    <context id="template-literals">
+      <start>`</start>
+      <end>`</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:template-literal"/>
+        <context sub-pattern="0" where="end" style-ref="js:template-literal"/>
+        <!-- no embedded-lang-hooks here -->
+        <!-- no comments here -->
+        <!-- do not match comments or embedded-lang-hooks here, because
+             template-head must match immediately -->
+        <context ref="_template-literal-content"/>
+      </include>
+    </context> <!-- /template-literals -->
+
+    <!-- <Template> / <TemplateLiteral> -->
+    <context id="choice-template-literal" end-parent="true">
+      <start>`</start>
+      <end>`</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:template-literal"/>
+        <context sub-pattern="0" where="end" style-ref="js:template-literal"/>
+        <!-- no embedded-lang-hooks here -->
+        <!-- no comments here -->
+        <!-- do not match comments or embedded-lang-hooks here, because
+             template-head must match immediately -->
+        <context ref="_template-literal-content"/>
+      </include>
+    </context> <!-- /choice-template-literal -->
+
+  </definitions>
+</language>
diff --git a/data/language-specs/javascript-modules.lang b/data/language-specs/javascript-modules.lang
new file mode 100644
index 00000000..57119030
--- /dev/null
+++ b/data/language-specs/javascript-modules.lang
@@ -0,0 +1,435 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ This file is part of GtkSourceView
+
+ Author: Scott Martin <scott coffeeblack org>
+ Copyright (C) 2004 Scott Martin <scott coffeeblack org>
+ Copyright (C) 2005 Stef Walter (formerly Nate Nielsen) <stef memberwebs com>
+ Copyright (C) 2005-2007 Marco Barisione <barisione gmail com>
+ Copyright (C) 2005-2007 Emanuele Aina
+ Copyright (C) 2019 Jeffery To <jeffery to gmail com>
+
+ GtkSourceView is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ GtkSourceView is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this library; if not, see <http://www.gnu.org/licenses/>.
+
+-->
+<language id="js-mod" name="JavaScript Modules" version="2.0" _section="Script" hidden="true">
+  <keyword-char-class>[a-zA-Z0-9_$]</keyword-char-class>
+
+  <definitions>
+
+    <!--
+         See javascript.lang for general notes, naming conventions, etc.
+    -->
+
+
+    <!-- # Shared between export and import declarations -->
+
+    <!-- ## (Import) From module
+
+         from 'module'
+    -->
+
+    <context id="_from-module" once-only="true">
+      <start>\%[from\%]</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:keyword"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_from-module-content">
+          <include>
+            <context ref="js-lit:choice-string-path"/>
+          </include>
+        </context> <!-- /_from-module-content -->
+
+      </include>
+    </context> <!-- /_from-module -->
+
+    <context id="_ordered-from-module" once-only="true">
+      <start>\%{js:before-next-token}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="_from-module"/>
+      </include>
+    </context> <!-- /_ordered-from-module -->
+
+    <!-- ## Export / import name group
+
+         { x, y as b }
+         { x as default }
+         { default }
+         { default as y }
+    -->
+
+    <context id="_name-group-identifier" once-only="true">
+      <start>(?=\%{js:identifier-start})</start>
+      <end>\%{def:always-match}</end>
+      <include>
+        <!-- no embedded-lang-hooks here -->
+        <!-- no comments here -->
+        <!-- do not extend the context by matching comments or
+             embedded-lang-hooks, which may lead to multiple identifiers -->
+
+        <context id="_name-group-identifier-content">
+          <include>
+
+            <context id="_name-group-keywords" style-ref="js:keyword">
+              <keyword>default</keyword>
+            </context> <!-- /_name-group-keywords -->
+
+            <context ref="js:identifier"/>
+          </include>
+        </context> <!-- /_name-group-identifier-content -->
+
+      </include>
+    </context> <!-- /_name-group-identifier -->
+
+    <context id="_name-group-as-identifier" once-only="true">
+      <start>\%[as\%]</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:keyword"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_name-group-as-identifier-content">
+          <include>
+            <context ref="_name-group-identifier"/>
+          </include>
+        </context> <!-- /_name-group-as-identifier-content -->
+
+      </include>
+    </context> <!-- /_name-group-as-identifier -->
+
+    <context id="_name-group-name-content">
+      <include>
+        <context ref="_name-group-identifier"/>
+        <context ref="_name-group-as-identifier"/>
+      </include>
+    </context> <!-- /_name-group-name-content -->
+
+    <!-- <ExportClause> / <NamedImports> -->
+    <context id="_name-group" once-only="true">
+      <start>{</start>
+      <end>}</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_name-group-content">
+          <include>
+
+            <context id="_name-group-first-name" once-only="true">
+              <start>\%{js:before-next-token}</start>
+              <end>\%{js:before-next-token}</end>
+              <include>
+                <context ref="js:embedded-lang-hooks"/>
+                <context ref="js:comments"/>
+                <context ref="_name-group-name-content"/>
+              </include>
+            </context> <!-- /_name-group-first-name -->
+
+            <context id="_name-group-names">
+              <start>,</start>
+              <end>\%{js:before-next-token}</end>
+              <include>
+                <context ref="js:embedded-lang-hooks"/>
+                <context ref="js:comments"/>
+                <context ref="_name-group-name-content"/>
+              </include>
+            </context> <!-- /_name-group-names -->
+
+          </include>
+        </context> <!-- /_name-group-content -->
+
+      </include>
+    </context> <!-- /_name-group -->
+
+    <context id="_ordered-name-group" once-only="true">
+      <start>\%{js:before-next-token}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="_name-group"/>
+      </include>
+    </context> <!-- /_ordered-name-group -->
+
+
+    <!-- # Export declaration -->
+
+    <!-- ## Export named
+
+         export { x, y as b };
+         export { x as default };
+         export { default } from 'module';
+    -->
+
+    <context id="_choice-export-named" end-parent="true">
+      <start>(?=\{)</start>
+      <end>\%{js:statement-end}</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_choice-export-named-content">
+          <include>
+            <context ref="_ordered-name-group"/>
+            <context ref="_ordered-from-module"/>
+          </include>
+        </context> <!-- /_choice-export-named-content -->
+
+      </include>
+    </context> <!-- /_choice-export-named -->
+
+    <!-- ## Export default
+
+         export default function () { ... }
+         export default class { ... }
+         export default expr;
+    -->
+
+    <context id="_choice-export-default" end-parent="true">
+      <start>\%[default\%]</start>
+      <end>\%{js:statement-end}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:keyword"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_export-default-content">
+          <include>
+            <context ref="js-fn:choice-class-expression"/>
+            <context ref="js-fn:choice-function-expression"/>
+            <context ref="js-expr:choice-expression-without-comma"/>
+          </include>
+        </context> <!-- /_export-default-content -->
+
+      </include>
+    </context> <!-- /_choice-export-default -->
+
+    <!-- ## Export all
+
+         export * from 'module';
+    -->
+
+    <context id="_choice-export-all" end-parent="true">
+      <start>\*</start>
+      <end>\%{js:statement-end}</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_export-all-content">
+          <include>
+            <context ref="_ordered-from-module"/>
+          </include>
+        </context> <!-- /_export-all-content -->
+
+      </include>
+    </context> <!-- /_choice-export-all -->
+
+    <!-- ## Export declaration
+
+         export { foo, bar as barry };
+         export const a = 1;
+         export default function () { ... }
+         export * from 'module';
+    -->
+
+    <!-- <ExportDeclaration> -->
+    <context id="export-declarations" style-ref="js:export-import-declaration">
+      <start>\%[export\%]</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:keyword"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_export-declaration-content">
+          <include>
+            <context ref="_choice-export-named"/>
+            <context ref="_choice-export-default"/>
+            <context ref="_choice-export-all"/>
+            <context ref="js-fn:choice-class-expression-required-name"/>
+            <context ref="js-fn:choice-function-expression"/>
+            <context ref="js-st:choice-variable-declaration"/>
+          </include>
+        </context> <!-- /_export-declaration-content -->
+
+      </include>
+    </context> <!-- /export-declarations -->
+
+
+    <!-- # Import declaration -->
+
+    <!-- ## Import named
+
+         import { default as y } from 'module';
+    -->
+
+    <context id="_choice-import-named" end-parent="true">
+      <start>(?=\{)</start>
+      <end>\%{js:statement-end}</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_choice-import-named-content">
+          <include>
+            <context ref="_ordered-name-group"/>
+            <context ref="_ordered-from-module"/>
+          </include>
+        </context> <!-- /_choice-import-named-content -->
+
+      </include>
+    </context> <!-- /_choice-import-named -->
+
+    <!-- ## Import as namespace
+
+         import * as nm from 'module';
+    -->
+
+    <context id="_as-namespace" once-only="true">
+      <start>\%[as\%]</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:keyword"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_as-namespace-content">
+          <include>
+            <context ref="js:identifier"/>
+          </include>
+        </context> <!-- /_as-namespace-content -->
+
+      </include>
+    </context> <!-- /_as-namespace -->
+
+    <context id="_ordered-as-namespace" once-only="true">
+      <start>\%{js:before-next-token}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="_as-namespace"/>
+      </include>
+    </context> <!-- /_ordered-as-namespace -->
+
+    <context id="_choice-import-as-namespace" end-parent="true">
+      <start>\*</start>
+      <end>\%{js:statement-end}</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_import-as-namespace-content">
+          <include>
+            <context ref="_ordered-as-namespace"/>
+            <context ref="_ordered-from-module"/>
+          </include>
+        </context> <!-- /_import-as-namespace-content -->
+
+      </include>
+    </context> <!-- /_choice-import-as-namespace -->
+
+    <!-- ## Import default
+
+         import def from 'module';
+         import def, { a, b } from 'module';
+         import def, * as nm from 'module';
+    -->
+
+    <context id="_choice-import-default-additional" end-parent="true">
+      <start>,</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_import-default-additional-content">
+          <include>
+            <context ref="_choice-import-named"/>
+            <context ref="_choice-import-as-namespace"/>
+          </include>
+        </context> <!-- /_import-default-additional-content -->
+
+      </include>
+    </context> <!-- /_choice-import-default-additional -->
+
+    <context id="_choice-import-default-only" end-parent="true">
+      <start>\%{js:before-next-token}</start>
+      <end>\%{js:statement-end}</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_import-default-only-content">
+          <include>
+            <context ref="_ordered-from-module"/>
+          </include>
+        </context> <!-- /_import-default-only-content -->
+
+      </include>
+    </context> <!-- /_choice-import-default-only -->
+
+    <context id="_choice-import-default" end-parent="true">
+      <start>(?=\%{js:identifier-start})</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_import-default-content">
+          <include>
+            <context ref="js:ordered-identifier"/>
+            <context ref="_choice-import-default-additional"/>
+            <context ref="_choice-import-default-only"/>
+          </include>
+        </context> <!-- /_import-default-content -->
+
+      </include>
+    </context> <!-- /_choice-import-default -->
+
+    <!-- ## Import declaration
+
+         import { foo as food, bar } from 'module';
+         import defaultFn, * as Mod from 'module';
+         import 'module';
+    -->
+
+    <!-- <ImportDeclaration> -->
+    <context id="import-declarations" style-ref="js:export-import-declaration">
+      <start extended="true">
+        \%[ import \%] (?! \%{js:import-function-keyword-suffix} )
+      </start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:keyword"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_import-declaration-content">
+          <include>
+            <context ref="_choice-import-named"/>
+            <context ref="_choice-import-as-namespace"/>
+            <context ref="_choice-import-default"/>
+            <context ref="js-lit:choice-string-path"/>
+          </include>
+        </context> <!-- /_import-declaration-content -->
+
+      </include>
+    </context> <!-- /import-declarations -->
+
+  </definitions>
+</language>
diff --git a/data/language-specs/javascript-statements.lang b/data/language-specs/javascript-statements.lang
new file mode 100644
index 00000000..25f0bbe5
--- /dev/null
+++ b/data/language-specs/javascript-statements.lang
@@ -0,0 +1,923 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ This file is part of GtkSourceView
+
+ Author: Scott Martin <scott coffeeblack org>
+ Copyright (C) 2004 Scott Martin <scott coffeeblack org>
+ Copyright (C) 2005 Stef Walter (formerly Nate Nielsen) <stef memberwebs com>
+ Copyright (C) 2005-2007 Marco Barisione <barisione gmail com>
+ Copyright (C) 2005-2007 Emanuele Aina
+ Copyright (C) 2019 Jeffery To <jeffery to gmail com>
+
+ GtkSourceView is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ GtkSourceView is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this library; if not, see <http://www.gnu.org/licenses/>.
+
+-->
+<language id="js-st" name="JavaScript Statements" version="2.0" _section="Script" hidden="true">
+  <keyword-char-class>[a-zA-Z0-9_$]</keyword-char-class>
+
+  <definitions>
+
+    <!--
+         See javascript.lang for general notes, naming conventions, etc.
+    -->
+
+
+    <!-- # Use strict directive -->
+
+    <!-- apparently, back references are not supported -->
+    <context id="_use-strict-directives" style-ref="js:directive">
+      <start extended="true">
+        (?:
+          " use [ ] strict " |
+          ' use [ ] strict '
+        )
+      </start>
+      <end>\%{js:statement-end}</end>
+    </context> <!-- /_use-strict-directives -->
+
+
+    <!-- # Directives -->
+
+    <!-- directives are valid at the start of scripts, modules, and
+         function bodies (but not block statements)
+    -->
+    <context id="directives">
+      <include>
+        <context ref="_use-strict-directives"/>
+      </include>
+    </context> <!-- /directives -->
+
+
+    <!-- # Test condition for if / switch / while / etc. -->
+
+    <context id="_condition" once-only="true">
+      <start>\(</start>
+      <end>\)</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments" />
+
+        <context id="_condition-content">
+          <include>
+            <context ref="js-expr:expression-with-comma"/>
+          </include>
+        </context> <!-- /_condition-content -->
+
+      </include>
+    </context> <!-- /_condition -->
+
+    <context id="_ordered-condition" once-only="true">
+      <start>\%{js:before-next-token}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="_condition"/>
+      </include>
+    </context> <!-- /_ordered-condition -->
+
+
+    <!-- # Block statement
+
+         {
+           ...
+         }
+    -->
+
+    <!-- <BlockStatement> -->
+    <context id="_block-statements" style-ref="js:block-statement">
+      <start>{</start>
+      <end>}</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_block-statement-content">
+          <include>
+            <context ref="statements"/>
+          </include>
+        </context> <!-- /_block-statement-content -->
+
+      </include>
+    </context> <!-- /_block-statements -->
+
+
+    <!-- # Break statement
+
+         break;
+         break outerLoop;
+    -->
+
+    <!-- <BreakStatement> -->
+    <context id="_break-statements" style-ref="js:break-statement">
+      <start>\%[break\%]</start>
+      <end>\%{js:statement-end-or-end-of-line}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:keyword"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments-no-extend-parent"/>
+
+        <context id="_break-statement-content">
+          <include>
+            <context ref="js:ordered-identifier"/>
+          </include>
+        </context> <!-- /_break-statement-content -->
+
+      </include>
+    </context> <!-- /_break-statements -->
+
+
+    <!-- # Class declaration
+
+         class Foo extends Bar { ... }
+    -->
+
+    <!-- <ClassExpression> / <ClassDeclaration> -->
+    <context id="_class-declarations">
+      <start>(?=\%{js:class-expression-keyword})</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_class-declaration-content">
+          <include>
+            <context ref="js-fn:choice-class-expression-required-name"/>
+          </include>
+        </context> <!-- /_class-declaration-content -->
+
+      </include>
+    </context> <!-- /_class-declarations -->
+
+
+    <!-- # Continue statement
+
+         continue;
+         continue outerLoop;
+    -->
+
+    <!-- <ContinueStatement> -->
+    <context id="_continue-statements" style-ref="js:continue-statement">
+      <start>\%[continue\%]</start>
+      <end>\%{js:statement-end-or-end-of-line}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:keyword"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments-no-extend-parent"/>
+
+        <context id="_continue-statement-content">
+          <include>
+            <context ref="js:ordered-identifier"/>
+          </include>
+        </context> <!-- /_continue-statement-content -->
+
+      </include>
+    </context> <!-- /_continue-statements -->
+
+
+    <!-- # Debugger statement
+
+         debugger;
+    -->
+
+    <!-- <DebuggerStatement> -->
+    <context id="_debugger-statements" style-ref="js:debugger-statement">
+      <start>\%[debugger\%]</start>
+      <end>\%{js:statement-end}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:keyword"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_debugger-statement-content"/>
+
+      </include>
+    </context> <!-- /_debugger-statements -->
+
+
+    <!-- # Expression statement
+
+         a = 1;
+         main();
+         i++;
+    -->
+
+    <!-- <ExpressionStatement> -->
+    <context id="_expression-statements" style-ref="js:expression-statement">
+      <start>\%{js:before-next-token}</start>
+      <end>\%{js:statement-end}</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_expression-statement-content">
+          <include>
+            <context ref="js-expr:expression-with-comma"/>
+          </include>
+        </context> <!-- /_expression-statement-content -->
+
+      </include>
+    </context> <!-- /_expression-statements -->
+
+
+    <!-- # For statement
+
+         for (var i = 0; i < length; i++) loop();
+         for (prop in obj) { ... }
+         for (value of iterator) { ... }
+    -->
+
+    <context id="_for-statement-modifier" once-only="true">
+      <start>\%{js:before-next-token}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_for-statement-modifier-content">
+          <include>
+
+            <context id="_choice-for-statement-modifier-keyword" style-ref="js:keyword" end-parent="true">
+              <keyword>await</keyword> <!-- ES2018 -->
+            </context> <!-- /_choice-for-statement-modifier-keyword -->
+
+          </include>
+        </context> <!-- /_for-statement-modifier-content -->
+
+      </include>
+    </context> <!-- /_for-statement-modifier -->
+
+    <context id="_ordered-for-statement-modifier" once-only="true">
+      <start>\%{js:before-next-token}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="_for-statement-modifier"/>
+      </include>
+    </context> <!-- /_ordered-for-statement-modifier -->
+
+    <context id="_for-statement-test">
+      <include>
+
+        <context id="_for-statement-test-initial-expression" once-only="true">
+          <start>\%{js:before-next-token}</start>
+          <end>\%{js:before-next-token}</end>
+          <include>
+            <context ref="js:embedded-lang-hooks"/>
+            <context ref="js:comments"/>
+
+            <context id="_for-statement-test-initial-expression-content">
+              <include>
+                <context ref="_choice-variable-declaration-without-semicolon"/>
+                <context ref="js-expr:choice-expression-with-comma"/>
+              </include>
+            </context> <!-- /_for-statement-test-initial-expression-content -->
+
+          </include>
+        </context> <!-- /_for-statement-test-initial-expression -->
+
+        <context id="_for-statement-test-condition" once-only="true">
+          <start>;</start>
+          <end>\%{js:before-next-token}</end>
+          <include>
+            <context ref="js:embedded-lang-hooks"/>
+            <context ref="js:comments"/>
+
+            <context id="_for-statement-test-condition-content">
+              <include>
+                <context ref="js-expr:expression-with-comma"/>
+              </include>
+            </context> <!-- /_for-statement-test-condition-content -->
+
+          </include>
+        </context> <!-- /_for-statement-test-condition -->
+
+        <context id="_for-statement-test-increment-expression" once-only="true">
+          <start>;</start>
+          <end>\%{js:before-next-token}</end>
+          <include>
+            <context ref="js:embedded-lang-hooks"/>
+            <context ref="js:comments"/>
+
+            <context id="_for-statement-test-increment-expression-content">
+              <include>
+                <context ref="js-expr:expression-with-comma"/>
+              </include>
+            </context> <!-- /_for-statement-test-increment-expression-content -->
+
+          </include>
+        </context> <!-- /_for-statement-test-increment-expression -->
+
+      </include>
+    </context> <!-- /_for-statement-test -->
+
+    <context id="_for-statement-in" once-only="true">
+      <start>\%[in\%]</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:keyword"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_for-statement-in-content">
+          <include>
+            <context ref="js-expr:expression-with-comma"/>
+          </include>
+        </context> <!-- /_for-statement-in-content -->
+
+      </include>
+    </context> <!-- /_for-statement-in -->
+
+    <context id="_for-statement-of" once-only="true">
+      <start>\%[of\%]</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:keyword"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_for-statement-of-content">
+          <include>
+            <context ref="js-expr:expression-without-comma"/>
+          </include>
+        </context> <!-- /_for-statement-of-content -->
+
+      </include>
+    </context> <!-- /_for-statement-of -->
+
+    <context id="_for-statement-condition" once-only="true">
+      <start>\(</start>
+      <end>\)</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_for-statement-condition-content">
+          <include>
+            <context ref="_for-statement-test"/>
+            <context ref="_for-statement-in"/>
+            <context ref="_for-statement-of"/>
+          </include>
+        </context> <!-- /_for-statement-condition-content -->
+
+      </include>
+    </context> <!-- /_for-statement-condition -->
+
+    <context id="_ordered-for-statement-condition" once-only="true">
+      <start>\%{js:before-next-token}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="_for-statement-condition"/>
+      </include>
+    </context> <!-- /_ordered-for-statement-condition -->
+
+    <!-- <IterationStatement> (part of) -->
+    <context id="_for-statements" style-ref="js:for-statement">
+      <start>\%[for\%]</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:keyword"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_for-statement-content">
+          <include>
+            <context ref="_ordered-for-statement-modifier"/>
+            <context ref="_ordered-for-statement-condition"/>
+          </include>
+        </context> <!-- /_for-statement-content -->
+
+      </include>
+    </context> <!-- /_for-statements -->
+
+
+    <!-- # Function declaration
+
+         function fn() { ... }
+    -->
+
+    <!-- <FunctionExpression> / <FunctionDeclaration> -->
+    <context id="_function-declarations">
+      <start>(?=\%{js:function-expression-keyword})</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_function-declaration-content">
+          <include>
+            <context ref="js-fn:choice-function-expression"/>
+          </include>
+        </context> <!-- /_function-declaration-content -->
+
+      </include>
+    </context> <!-- /_function-declarations -->
+
+
+    <!-- # If...else statement
+
+         if (done) return;
+         if (i > 0) { ... } else if (i < 0) { ... } else { ... }
+    -->
+
+    <!-- <IfStatement> (part of) -->
+    <context id="_if-statements" style-ref="js:if-statement">
+      <start>\%[if\%]</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:keyword"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_if-statement-content">
+          <include>
+            <context ref="_ordered-condition"/>
+          </include>
+        </context> <!-- /_if-statement-content -->
+
+      </include>
+    </context> <!-- /_if-statements -->
+
+    <!-- <IfStatement> (part of) -->
+    <context id="_else-statements" style-ref="js:else-statement">
+      <start>\%[else\%]</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:keyword"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_else-statement-content"/>
+
+      </include>
+    </context> <!-- /_else-statements -->
+
+
+    <!-- # Label statement
+
+         loop: for (...) {...}
+    -->
+
+    <!-- <LabelledStatement> -->
+    <!-- technically, only statements (and function declarations in
+         non-strict mode) are allowed to follow a label identifier, but
+         let's just end the label statement after the colon
+    -->
+    <context id="_label-statements" style-ref="js:label-statement">
+      <start extended="true">
+        (?=
+          \%{js:identifier}
+          \%{js:optional-whitespace-or-comments}
+          :
+        )
+      </start>
+      <end>:</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_label-statement-content">
+          <include>
+            <!-- technically, yield and await are valid label identifiers
+                 in non-strict mode and non-module code, respectively
+                 but let's not allow it (and have them highlighted as
+                 reserved words)
+            -->
+            <context ref="js:ordered-identifier"/>
+          </include>
+        </context> <!-- /_label-statement-content -->
+
+      </include>
+    </context> <!-- /_label-statements -->
+
+
+    <!-- # Return statement
+
+         return;
+         return value;
+    -->
+
+    <!-- <ReturnStatement> -->
+    <context id="_return-statements" style-ref="js:return-statement">
+      <start>\%[return\%]</start>
+      <end>\%{js:statement-end-or-end-of-line}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:keyword"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments-no-extend-parent"/>
+
+        <context id="_return-statement-content">
+          <include>
+            <context ref="js-expr:expression-with-comma"/>
+          </include>
+        </context> <!-- /_return-statement-content -->
+
+      </include>
+    </context> <!-- /_return-statements -->
+
+
+    <!-- # Switch statement
+
+         switch (a) {
+         case 1: ... ; break;
+         case 2: ... ; break;
+         default: ...
+         }
+    -->
+
+    <context id="_case-clauses">
+      <start>\%[case\%]</start>
+      <end>:</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:keyword"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_case-clause-content">
+          <include>
+            <context ref="js-expr:expression-with-comma"/>
+          </include>
+        </context> <!-- /_case-clause-content -->
+
+      </include>
+    </context> <!-- /_case-clauses -->
+
+    <context id="_default-clauses">
+      <start>\%[default\%]</start>
+      <end>:</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:keyword"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_default-clause-content"/>
+
+      </include>
+    </context> <!-- /_default-clauses -->
+
+    <context id="_switch-body" once-only="true">
+      <start>{</start>
+      <end>}</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_switch-body-content">
+          <include>
+            <context ref="_case-clauses"/>
+            <context ref="_default-clauses"/>
+            <context ref="statements"/>
+          </include>
+        </context> <!-- /_switch-body-content -->
+
+      </include>
+    </context> <!-- /_switch-body -->
+
+    <context id="_last-switch-body" end-parent="true">
+      <start>\%{js:before-next-token}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="_switch-body"/>
+      </include>
+    </context> <!-- /_last-switch-body -->
+
+    <!-- <SwitchStatement> -->
+    <context id="_switch-statements" style-ref="js:switch-statement">
+      <start>\%[switch\%]</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:keyword"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_switch-statement-content">
+          <include>
+            <context ref="_ordered-condition"/>
+            <context ref="_last-switch-body"/>
+          </include>
+        </context> <!-- /_switch-statement-content -->
+
+      </include>
+    </context> <!-- /_switch-statements -->
+
+
+    <!-- # Throw statement
+
+         throw new Error();
+    -->
+
+    <!-- <ThrowStatement> -->
+    <context id="_throw-statements" style-ref="js:throw-statement">
+      <start>\%[throw\%]</start>
+      <end>\%{js:statement-end-or-end-of-line}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:keyword"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments-no-extend-parent"/>
+
+        <context id="_throw-statement-content">
+          <include>
+            <context ref="js-expr:expression-with-comma"/>
+          </include>
+        </context> <!-- /_throw-statement-content -->
+
+      </include>
+    </context> <!-- /_throw-statements -->
+
+
+    <!-- # Try...catch statement
+
+         try { ... } catch (e) { ... } finally { ... }
+    -->
+
+    <context id="_try-body" once-only="true">
+      <start>{</start>
+      <end>}</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_try-body-content">
+          <include>
+            <context ref="statements"/>
+          </include>
+        </context> <!-- /_try-body-content -->
+
+      </include>
+    </context> <!-- /_try-body -->
+
+    <context id="_last-try-body" end-parent="true">
+      <start>\%{js:before-next-token}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="_try-body"/>
+      </include>
+    </context> <!-- /_last-try-body -->
+
+    <!-- <TryStatement> -->
+    <context id="_try-statements" style-ref="js:try-catch-statement">
+      <start>\%[try\%]</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:keyword"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_try-statement-content">
+          <include>
+            <context ref="_last-try-body"/>
+          </include>
+        </context> <!-- /_try-statement-content -->
+
+      </include>
+    </context> <!-- /_try-statements -->
+
+    <context id="_catch-statement-exception" once-only="true">
+      <start>\(</start>
+      <end>\)</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_catch-statement-exception-content">
+          <include>
+            <context ref="js:identifier"/>
+          </include>
+        </context> <!-- /_catch-statement-exception-content -->
+
+      </include>
+    </context> <!-- /_catch-statement-exception -->
+
+    <context id="_ordered-catch-statement-exception" once-only="true">
+      <start>\%{js:before-next-token}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="_catch-statement-exception"/>
+      </include>
+    </context> <!-- /_ordered-catch-statement-exception -->
+
+    <!-- <Catch> -->
+    <context id="_catch-statements" style-ref="js:try-catch-statement">
+      <start>\%[catch\%]</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:keyword"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_catch-statement-content">
+          <include>
+            <context ref="_ordered-catch-statement-exception"/>
+            <context ref="_last-try-body"/>
+          </include>
+        </context> <!-- /_catch-statement-content -->
+
+      </include>
+    </context> <!-- /_catch-statements -->
+
+    <!-- <Finally> -->
+    <context id="_finally-statements" style-ref="js:try-catch-statement">
+      <start>\%[finally\%]</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:keyword"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_finally-statement-content">
+          <include>
+            <context ref="_last-try-body"/>
+          </include>
+        </context> <!-- /_finally-statement-content -->
+
+      </include>
+    </context> <!-- /_finally-statements -->
+
+
+    <!-- # Variable declaration
+
+         var a, b = 2;
+         let [a, b] = [1, 2];
+         const { a, y: b = 4, c = 5 } = { a: 1, y: 2 };
+    -->
+
+    <define-regex id="_variable-declaration-keyword" extended="true">
+      (?: \%[ (?: const | let | var ) \%] )
+    </define-regex> <!-- /_variable-declaration-keyword -->
+
+    <context id="_variable-declaration-item-content">
+      <include>
+        <context ref="js:ordered-assignment-target"/>
+        <context ref="js:ordered-default-value-assignment"/>
+      </include>
+    </context> <!-- /_variable-declaration-item-content -->
+
+    <context id="_variable-declaration-content">
+      <include>
+
+        <context id="_variable-declaration-first-item" once-only="true">
+          <start>\%{js:before-next-token}</start>
+          <end>\%{js:before-next-token}</end>
+          <include>
+            <context ref="js:embedded-lang-hooks"/>
+            <context ref="js:comments"/>
+            <context ref="_variable-declaration-item-content"/>
+          </include>
+        </context> <!-- /_variable-declaration-first-item -->
+
+        <context id="_variable-declaration-items">
+          <start>,</start>
+          <end>\%{js:before-next-token}</end>
+          <include>
+            <context ref="js:embedded-lang-hooks"/>
+            <context ref="js:comments"/>
+            <context ref="_variable-declaration-item-content"/>
+          </include>
+        </context> <!-- /_variable-declaration-items -->
+
+      </include>
+    </context> <!-- /_variable-declaration-content -->
+
+    <!-- <VariableStatement> / <LexicalDeclaration> -->
+    <context id="_variable-declarations" style-ref="js:variable-declaration">
+      <start>\%{_variable-declaration-keyword}</start>
+      <end>\%{js:statement-end}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:keyword"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+        <context ref="_variable-declaration-content"/>
+      </include>
+    </context> <!-- /_variable-declarations -->
+
+    <!-- <VariableStatement> / <LexicalDeclaration> -->
+    <context id="choice-variable-declaration" style-ref="js:variable-declaration" end-parent="true">
+      <start>\%{_variable-declaration-keyword}</start>
+      <end>\%{js:statement-end}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:keyword"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+        <context ref="_variable-declaration-content"/>
+      </include>
+    </context> <!-- /choice-variable-declaration -->
+
+    <!-- <VariableStatement> / <LexicalDeclaration> -->
+    <context id="_choice-variable-declaration-without-semicolon" style-ref="js:variable-declaration" 
end-parent="true">
+      <start>\%{_variable-declaration-keyword}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:keyword"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+        <context ref="_variable-declaration-content"/>
+      </include>
+    </context> <!-- /_choice-variable-declaration-without-semicolon -->
+
+
+    <!-- # While / do...while statement
+
+         while (true) loop();
+         do { ... } while (a > 0);
+    -->
+
+    <!-- <IterationStatement> (part of) -->
+    <context id="_while-statements" style-ref="js:while-statement">
+      <start>\%[while\%]</start>
+      <end>\%{js:statement-end}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:keyword"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_while-statement-content">
+          <include>
+            <context ref="_ordered-condition"/>
+          </include>
+        </context> <!-- /_while-statement-content -->
+
+      </include>
+    </context> <!-- /_while-statements -->
+
+    <!-- <IterationStatement> (part of) -->
+    <context id="_do-statements" style-ref="js:while-statement">
+      <start>\%[do\%]</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:keyword"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_do-statement-content"/>
+
+      </include>
+    </context> <!-- /_do-statements -->
+
+
+    <!-- # With statement
+
+         with (o) doIt();
+         with (obj) { ... }
+    -->
+
+    <!-- <WithStatement> -->
+    <context id="_with-statements" style-ref="js:with-statement">
+      <start>\%[with\%]</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:keyword"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_with-statement-content">
+          <include>
+            <context ref="_ordered-condition"/>
+          </include>
+        </context> <!-- /_with-statement-content -->
+
+      </include>
+    </context> <!-- /_with-statements -->
+
+
+    <!-- # Statements -->
+
+    <!-- <StatementList> -->
+    <!-- statements and declarations that are valid in "scripts" (as
+         opposed to modules) and in block statements -->
+    <context id="statements">
+      <include>
+        <context ref="_block-statements"/>
+        <context ref="_break-statements"/>
+        <context ref="_class-declarations"/>
+        <context ref="_continue-statements"/>
+        <context ref="_debugger-statements"/>
+        <context ref="_for-statements"/>
+        <context ref="_function-declarations"/>
+        <context ref="_if-statements"/>
+        <context ref="_else-statements"/>
+        <context ref="_label-statements"/>
+        <context ref="_return-statements"/>
+        <context ref="_switch-statements"/>
+        <context ref="_throw-statements"/>
+        <context ref="_try-statements"/>
+        <context ref="_catch-statements"/>
+        <context ref="_finally-statements"/>
+        <context ref="_variable-declarations"/>
+        <context ref="_while-statements"/>
+        <context ref="_do-statements"/>
+        <context ref="_with-statements"/>
+        <context ref="_expression-statements"/> <!-- catch-all -->
+      </include>
+    </context> <!-- /statements -->
+
+  </definitions>
+</language>
diff --git a/data/language-specs/javascript-values.lang b/data/language-specs/javascript-values.lang
new file mode 100644
index 00000000..c1e41d5a
--- /dev/null
+++ b/data/language-specs/javascript-values.lang
@@ -0,0 +1,697 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ This file is part of GtkSourceView
+
+ Author: Scott Martin <scott coffeeblack org>
+ Copyright (C) 2004 Scott Martin <scott coffeeblack org>
+ Copyright (C) 2005 Stef Walter (formerly Nate Nielsen) <stef memberwebs com>
+ Copyright (C) 2005-2007 Marco Barisione <barisione gmail com>
+ Copyright (C) 2005-2007 Emanuele Aina
+ Copyright (C) 2019 Jeffery To <jeffery to gmail com>
+
+ GtkSourceView is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ GtkSourceView is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this library; if not, see <http://www.gnu.org/licenses/>.
+
+-->
+<language id="js-val" name="JavaScript Built-in Values" version="2.0" _section="Script" hidden="true">
+  <keyword-char-class>[a-zA-Z0-9_$]</keyword-char-class>
+
+  <definitions>
+
+    <!--
+         See javascript.lang for general notes, naming conventions, etc.
+    -->
+
+
+    <!-- # Global values -->
+
+    <context id="global-values">
+      <include>
+
+        <!--
+            There was a long discussion on ##javascript on freenode between
+            'katspaugh', 'joo' and 'prog_' on whether 'undefined' should be
+            highlighted on not, specialy as a constant. The conclusion was "It can't
+            be highlighted as a constant literal value, because it can be an
+            identifier (of a variable value) but leave it be as it is. Let tradition
+            and convention obscure the details."
+        -->
+
+        <!-- 18.1 Value Properties of the Global Object -->
+        <context id="_values" style-ref="js:built-in-value">
+          <keyword>Infinity</keyword>
+          <keyword>NaN</keyword>
+          <keyword>undefined</keyword>
+        </context> <!-- /_values -->
+
+        <!-- 18.2 Function Properties of the Global Object -->
+        <context id="_functions" style-ref="js:built-in-function">
+          <keyword>decodeURIComponent</keyword>
+          <keyword>decodeURI</keyword>
+          <keyword>encodeURIComponent</keyword>
+          <keyword>encodeURI</keyword>
+          <keyword>eval</keyword>
+          <keyword>isFinite</keyword>
+          <keyword>isNaN</keyword>
+          <keyword>parseFloat</keyword>
+          <keyword>parseInt</keyword>
+        </context> <!-- /_functions -->
+
+        <context id="_constructors" style-ref="js:built-in-constructor">
+          <keyword>ArrayBuffer</keyword>
+          <keyword>Array</keyword>
+          <keyword>BigInt</keyword> <!-- ES2020 -->
+          <keyword>Boolean</keyword>
+          <keyword>Date</keyword>
+          <keyword>Error</keyword>
+          <keyword>EvalError</keyword>
+          <keyword>Float32Array</keyword>
+          <keyword>Float64Array</keyword>
+          <keyword>Function</keyword>
+          <keyword>Int16Array</keyword>
+          <keyword>Int32Array</keyword>
+          <keyword>Int8Array</keyword>
+          <keyword>Map</keyword>
+          <keyword>Number</keyword>
+          <keyword>Object</keyword>
+          <keyword>Promise</keyword>
+          <keyword>Proxy</keyword>
+          <keyword>RangeError</keyword>
+          <keyword>ReferenceError</keyword>
+          <keyword>Reflect</keyword>
+          <keyword>RegExp</keyword>
+          <keyword>Set</keyword>
+          <keyword>String</keyword>
+          <keyword>Symbol</keyword>
+          <keyword>SyntaxError</keyword>
+          <keyword>TypeError</keyword>
+          <keyword>Uint16Array</keyword>
+          <keyword>Uint32Array</keyword>
+          <keyword>Uint8Array</keyword>
+          <keyword>Uint8ClampedArray</keyword>
+          <keyword>URIError</keyword>
+          <keyword>WeakMap</keyword>
+          <keyword>WeakSet</keyword>
+        </context> <!-- /_constructors -->
+
+        <context id="_objects" style-ref="js:built-in-object">
+          <keyword>Intl</keyword>
+          <keyword>JSON</keyword>
+          <keyword>Math</keyword>
+          <keyword>WebAssembly</keyword>
+        </context> <!-- /_objects -->
+
+        <context id="_object-keywords" style-ref="js:keyword">
+          <keyword>arguments</keyword>
+          <keyword>globalThis</keyword> <!-- ES2020 -->
+          <keyword>super</keyword>
+          <keyword>this</keyword>
+        </context> <!-- /_object-keywords -->
+
+        <context id="_import-function-keyword">
+          <start extended="true">
+            \%[ import \%] (?= \%{js:import-function-keyword-suffix} )
+          </start>
+          <end>(?=\()</end>
+          <include>
+            <context sub-pattern="0" where="start" style-ref="js:keyword"/>
+            <!-- no embedded-lang-hooks here -->
+            <context ref="js:comments"/>
+          </include>
+        </context> <!-- /_import-function-keyword -->
+
+        <context id="_new-target-object-keyword">
+          <start extended="true">
+            \%[ new \%] (?= \%{js:new-target-object-keyword-suffix} )
+          </start>
+          <end>\%[target\%]</end>
+          <include>
+            <context sub-pattern="0" where="start" style-ref="js:keyword"/>
+            <context sub-pattern="0" where="end" style-ref="js:keyword"/>
+            <!-- no embedded-lang-hooks here -->
+            <context ref="js:comments"/>
+
+            <context id="_new-target-object-keyword-content">
+              <include>
+                <context ref="js:ordered-keyword-dot"/>
+              </include>
+            </context> <!-- /_new-target-object-keyword-content -->
+
+          </include>
+        </context> <!-- /_new-target-object-keyword -->
+
+      </include>
+    </context> <!-- /global-values -->
+
+
+    <!-- # Properties / methods -->
+
+    <context id="properties-methods">
+      <include>
+
+        <context id="_intl-constructors" style-ref="js:built-in-constructor">
+          <keyword>Collator</keyword>
+          <keyword>DateTimeFormat</keyword>
+          <keyword>NumberFormat</keyword>
+          <keyword>PluralRules</keyword>
+        </context> <!-- /_intl-constructors -->
+
+        <context id="_webassembly-constructors" style-ref="js:built-in-constructor">
+          <keyword>CompileError</keyword>
+          <keyword>Global</keyword>
+          <keyword>Instance</keyword>
+          <keyword>LinkError</keyword>
+          <keyword>Memory</keyword>
+          <keyword>Module</keyword>
+          <keyword>RuntimeError</keyword>
+          <keyword>Table</keyword>
+        </context> <!-- /_webassembly-constructors -->
+
+        <context id="_array-properties" style-ref="js:built-in-property">
+          <keyword>length</keyword>
+        </context> <!-- /_array-properties -->
+
+        <context id="_array-methods" style-ref="js:built-in-method">
+          <keyword>concat</keyword>
+          <keyword>copyWithin</keyword>
+          <keyword>entries</keyword>
+          <keyword>every</keyword>
+          <keyword>fill</keyword>
+          <keyword>filter</keyword>
+          <keyword>findIndex</keyword>
+          <keyword>find</keyword>
+          <keyword>flatMap</keyword> <!-- ES2019 -->
+          <keyword>flat</keyword> <!-- ES2019 -->
+          <keyword>forEach</keyword>
+          <keyword>from</keyword>
+          <keyword>includes</keyword> <!-- ES2016 -->
+          <keyword>indexOf</keyword>
+          <keyword>isArray</keyword>
+          <keyword>join</keyword>
+          <keyword>keys</keyword>
+          <keyword>lastIndexOf</keyword>
+          <keyword>map</keyword>
+          <keyword>of</keyword>
+          <keyword>pop</keyword>
+          <keyword>push</keyword>
+          <keyword>reduceRight</keyword>
+          <keyword>reduce</keyword>
+          <keyword>reverse</keyword>
+          <keyword>shift</keyword>
+          <keyword>slice</keyword>
+          <keyword>some</keyword>
+          <keyword>sort</keyword>
+          <keyword>unshift</keyword>
+          <keyword>values</keyword>
+        </context> <!-- /_array-methods -->
+
+        <context id="_arraybuffer-properties" style-ref="js:built-in-property">
+          <keyword>byteLength</keyword>
+        </context> <!-- /_arraybuffer-properties -->
+
+        <context id="_arraybuffer-methods" style-ref="js:built-in-method">
+          <keyword>isView</keyword>
+          <keyword>slice</keyword>
+        </context> <!-- /_arraybuffer-methods -->
+
+        <!-- ES2020 -->
+        <context id="_bigint-methods" style-ref="js:built-in-method">
+          <keyword>asIntN</keyword>
+          <keyword>asUintN</keyword>
+        </context> <!-- /_bigint-methods -->
+
+        <context id="_dataview-properties" style-ref="js:built-in-property">
+          <keyword>buffer</keyword>
+          <keyword>byteLength</keyword>
+          <keyword>byteOffset</keyword>
+        </context> <!-- /_dataview-properties -->
+
+        <context id="_dataview-methods" style-ref="js:built-in-method">
+          <keyword>getFloat32</keyword>
+          <keyword>getFloat64</keyword>
+          <keyword>getInt16</keyword>
+          <keyword>getInt32</keyword>
+          <keyword>getInt8</keyword>
+          <keyword>getUint16</keyword>
+          <keyword>getUint32</keyword>
+          <keyword>getUint8</keyword>
+          <keyword>setFloat32</keyword>
+          <keyword>setFloat64</keyword>
+          <keyword>setInt16</keyword>
+          <keyword>setInt32</keyword>
+          <keyword>setInt8</keyword>
+          <keyword>setUint16</keyword>
+          <keyword>setUint32</keyword>
+          <keyword>setUint8</keyword>
+        </context> <!-- /_dataview-methods -->
+
+        <context id="_date-methods" style-ref="js:built-in-method">
+          <keyword>getDate</keyword>
+          <keyword>getDay</keyword>
+          <keyword>getFullYear</keyword>
+          <keyword>getHours</keyword>
+          <keyword>getMilliseconds</keyword>
+          <keyword>getMinutes</keyword>
+          <keyword>getMonth</keyword>
+          <keyword>getSeconds</keyword>
+          <keyword>getTime</keyword>
+          <keyword>getTimezoneOffset</keyword>
+          <keyword>getUTCDate</keyword>
+          <keyword>getUTCDay</keyword>
+          <keyword>getUTCFullYear</keyword>
+          <keyword>getUTCHours</keyword>
+          <keyword>getUTCMilliseconds</keyword>
+          <keyword>getUTCMinutes</keyword>
+          <keyword>getUTCMonth</keyword>
+          <keyword>getUTCSeconds</keyword>
+          <keyword>now</keyword>
+          <keyword>parse</keyword>
+          <keyword>setDate</keyword>
+          <keyword>setFullYear</keyword>
+          <keyword>setHours</keyword>
+          <keyword>setMilliseconds</keyword>
+          <keyword>setMinutes</keyword>
+          <keyword>setMonth</keyword>
+          <keyword>setSeconds</keyword>
+          <keyword>setTime</keyword>
+          <keyword>setUTCDate</keyword>
+          <keyword>setUTCFullYear</keyword>
+          <keyword>setUTCHours</keyword>
+          <keyword>setUTCMilliseconds</keyword>
+          <keyword>setUTCMinutes</keyword>
+          <keyword>setUTCMonth</keyword>
+          <keyword>setUTCSeconds</keyword>
+          <keyword>toDateString</keyword>
+          <keyword>toISOString</keyword>
+          <keyword>toJSON</keyword>
+          <keyword>toLocaleDateString</keyword>
+          <keyword>toLocaleTimeString</keyword>
+          <keyword>toTimeString</keyword>
+          <keyword>toUTCString</keyword>
+          <keyword>UTC</keyword>
+        </context> <!-- /_date-methods -->
+
+        <context id="_error-properties" style-ref="js:built-in-property">
+          <keyword>message</keyword>
+          <keyword>name</keyword>
+        </context> <!-- /_error-properties -->
+
+        <context id="_function-properties" style-ref="js:built-in-property">
+          <keyword>length</keyword>
+          <keyword>name</keyword>
+        </context> <!-- /_function-properties -->
+
+        <context id="_function-methods" style-ref="js:built-in-method">
+          <keyword>apply</keyword>
+          <keyword>bind</keyword>
+          <keyword>call</keyword>
+        </context> <!-- /_function-methods -->
+
+        <context id="_generator-methods" style-ref="js:built-in-method">
+          <keyword>next</keyword>
+          <keyword>return</keyword>
+          <keyword>throw</keyword>
+        </context> <!-- /_generator-methods -->
+
+        <context id="_intl-methods" style-ref="js:built-in-method">
+          <keyword>formatToParts</keyword> <!-- common to intl format object instances -->
+          <keyword>format</keyword> <!-- common to intl format object instances -->
+          <keyword>getCanonicalLocales</keyword>
+          <keyword>resolvedOptions</keyword> <!-- common to intl object instances -->
+          <keyword>supportedLocalesOf</keyword> <!-- common to intl objects -->
+        </context> <!-- /_intl-methods -->
+
+        <context id="_intl-collator-methods" style-ref="js:built-in-method">
+          <keyword>compare</keyword>
+        </context> <!-- /_intl-collator-methods -->
+
+        <context id="_intl-pluralrules-methods" style-ref="js:built-in-method">
+          <keyword>select</keyword>
+        </context> <!-- /_intl-pluralrules-methods -->
+
+        <context id="_json-methods" style-ref="js:built-in-method">
+          <keyword>parse</keyword>
+          <keyword>stringify</keyword>
+        </context> <!-- /_json-methods -->
+
+        <context id="_map-properties" style-ref="js:built-in-property">
+          <keyword>size</keyword>
+        </context> <!-- /_map-properties -->
+
+        <context id="_map-methods" style-ref="js:built-in-method">
+          <keyword>clear</keyword>
+          <keyword>delete</keyword>
+          <keyword>entries</keyword>
+          <keyword>forEach</keyword>
+          <keyword>get</keyword>
+          <keyword>has</keyword>
+          <keyword>keys</keyword>
+          <keyword>set</keyword>
+          <keyword>values</keyword>
+        </context> <!-- /_map-methods -->
+
+        <context id="_math-properties" style-ref="js:built-in-property">
+          <keyword>E</keyword>
+          <keyword>LN10</keyword>
+          <keyword>LN2</keyword>
+          <keyword>LOG10E</keyword>
+          <keyword>LOG2E</keyword>
+          <keyword>PI</keyword>
+          <keyword>SQRT1_2</keyword>
+          <keyword>SQRT2</keyword>
+        </context> <!-- /_math-properties -->
+
+        <context id="_math-methods" style-ref="js:built-in-method">
+          <keyword>abs</keyword>
+          <keyword>acosh</keyword>
+          <keyword>acos</keyword>
+          <keyword>asinh</keyword>
+          <keyword>asin</keyword>
+          <keyword>atan2</keyword>
+          <keyword>atanh</keyword>
+          <keyword>atan</keyword>
+          <keyword>cbrt</keyword>
+          <keyword>ceil</keyword>
+          <keyword>clz32</keyword>
+          <keyword>cosh</keyword>
+          <keyword>cos</keyword>
+          <keyword>expm1</keyword>
+          <keyword>exp</keyword>
+          <keyword>floor</keyword>
+          <keyword>fround</keyword>
+          <keyword>hypot</keyword>
+          <keyword>imul</keyword>
+          <keyword>log10</keyword>
+          <keyword>log1p</keyword>
+          <keyword>log2</keyword>
+          <keyword>log</keyword>
+          <keyword>max</keyword>
+          <keyword>min</keyword>
+          <keyword>pow</keyword>
+          <keyword>random</keyword>
+          <keyword>round</keyword>
+          <keyword>sign</keyword>
+          <keyword>sinh</keyword>
+          <keyword>sin</keyword>
+          <keyword>sqrt</keyword>
+          <keyword>tanh</keyword>
+          <keyword>tan</keyword>
+          <keyword>trunc</keyword>
+        </context> <!-- /_math-methods -->
+
+        <context id="_number-properties" style-ref="js:built-in-property">
+          <keyword>EPSILON</keyword>
+          <keyword>MAX_SAFE_INTEGER</keyword>
+          <keyword>MAX_VALUE</keyword>
+          <keyword>MIN_SAFE_INTEGER</keyword>
+          <keyword>MIN_VALUE</keyword>
+          <keyword>NaN</keyword>
+          <keyword>NEGATIVE_INFINITY</keyword>
+          <keyword>POSITIVE_INFINITY</keyword>
+        </context> <!-- /_number-properties -->
+
+        <context id="_number-methods" style-ref="js:built-in-method">
+          <keyword>isFinite</keyword>
+          <keyword>isInteger</keyword>
+          <keyword>isNaN</keyword>
+          <keyword>isSafeInteger</keyword>
+          <keyword>parseFloat</keyword>
+          <keyword>parseInt</keyword>
+          <keyword>toExponential</keyword>
+          <keyword>toFixed</keyword>
+          <keyword>toPrecision</keyword>
+        </context> <!-- /_number-methods -->
+
+        <context id="_object-properties" style-ref="js:built-in-property">
+          <keyword>constructor</keyword>
+          <keyword>prototype</keyword>
+        </context> <!-- /_object-properties -->
+
+        <context id="_object-methods" style-ref="js:built-in-method">
+          <keyword>assign</keyword>
+          <keyword>create</keyword>
+          <keyword>defineProperties</keyword>
+          <keyword>defineProperty</keyword>
+          <keyword>entries</keyword> <!-- ES2017 -->
+          <keyword>freeze</keyword>
+          <keyword>fromEntries</keyword> <!-- ES2019 -->
+          <keyword>getOwnPropertyDescriptors</keyword> <!-- ES2017 -->
+          <keyword>getOwnPropertyDescriptor</keyword>
+          <keyword>getOwnPropertyNames</keyword>
+          <keyword>getOwnPropertySymbols</keyword>
+          <keyword>getPrototypeOf</keyword>
+          <keyword>hasOwnProperty</keyword>
+          <keyword>isExtensible</keyword>
+          <keyword>isFrozen</keyword>
+          <keyword>isPrototypeOf</keyword>
+          <keyword>isSealed</keyword>
+          <keyword>is</keyword>
+          <keyword>keys</keyword>
+          <keyword>preventExtensions</keyword>
+          <keyword>propertyIsEnumerable</keyword>
+          <keyword>seal</keyword>
+          <keyword>setPrototypeOf</keyword>
+          <keyword>toLocaleString</keyword>
+          <keyword>toString</keyword>
+          <keyword>valueOf</keyword>
+          <keyword>values</keyword> <!-- ES2017 -->
+        </context> <!-- /_object-methods -->
+
+        <context id="_promise-methods" style-ref="js:built-in-method">
+          <keyword>allSettled</keyword> <!-- ES2020 -->
+          <keyword>all</keyword>
+          <keyword>catch</keyword>
+          <keyword>finally</keyword> <!-- ES2018 -->
+          <keyword>race</keyword>
+          <keyword>reject</keyword>
+          <keyword>resolve</keyword>
+          <keyword>then</keyword>
+        </context> <!-- /_promise-methods -->
+
+        <context id="_proxy-methods" style-ref="js:built-in-method">
+          <keyword>revocable</keyword>
+        </context> <!-- /_proxy-methods -->
+
+        <context id="_reflect-methods" style-ref="js:built-in-method">
+          <keyword>apply</keyword>
+          <keyword>construct</keyword>
+          <keyword>defineProperty</keyword>
+          <keyword>deleteProperty</keyword>
+          <keyword>getOwnPropertyDescriptor</keyword>
+          <keyword>getPrototypeOf</keyword>
+          <keyword>get</keyword>
+          <keyword>has</keyword>
+          <keyword>isExtensible</keyword>
+          <keyword>ownKeys</keyword>
+          <keyword>preventExtensions</keyword>
+          <keyword>setPrototypeOf</keyword>
+          <keyword>set</keyword>
+        </context> <!-- /_reflect-methods -->
+
+        <context id="_regexp-properties" style-ref="js:built-in-property">
+          <keyword>flags</keyword>
+          <keyword>global</keyword>
+          <keyword>ignoreCase</keyword>
+          <keyword>lastIndex</keyword>
+          <keyword>multiline</keyword>
+          <keyword>source</keyword>
+          <keyword>sticky</keyword>
+          <keyword>unicode</keyword>
+        </context> <!-- /_regexp-properties -->
+
+        <context id="_regexp-methods" style-ref="js:built-in-method">
+          <keyword>exec</keyword>
+          <keyword>test</keyword>
+        </context> <!-- /_regexp-methods -->
+
+        <context id="_set-properties" style-ref="js:built-in-property">
+          <keyword>size</keyword>
+        </context> <!-- /_set-properties -->
+
+        <context id="_set-methods" style-ref="js:built-in-method">
+          <keyword>add</keyword>
+          <keyword>clear</keyword>
+          <keyword>delete</keyword>
+          <keyword>entries</keyword>
+          <keyword>forEach</keyword>
+          <keyword>has</keyword>
+          <keyword>keys</keyword>
+          <keyword>values</keyword>
+        </context> <!-- /_set-methods -->
+
+        <context id="_string-properties" style-ref="js:built-in-property">
+          <keyword>length</keyword>
+        </context> <!-- /_string-properties -->
+
+        <context id="_string-methods" style-ref="js:built-in-method">
+          <keyword>charAt</keyword>
+          <keyword>charCodeAt</keyword>
+          <keyword>codePointAt</keyword>
+          <keyword>concat</keyword>
+          <keyword>endsWith</keyword>
+          <keyword>fromCharCode</keyword>
+          <keyword>fromCodePoint</keyword>
+          <keyword>includes</keyword>
+          <keyword>indexOf</keyword>
+          <keyword>lastIndexOf</keyword>
+          <keyword>localeCompare</keyword>
+          <keyword>matchAll</keyword> <!-- ES2020 -->
+          <keyword>match</keyword>
+          <keyword>normalize</keyword>
+          <keyword>padEnd</keyword> <!-- ES2017 -->
+          <keyword>padStart</keyword> <!-- ES2017 -->
+          <keyword>raw</keyword>
+          <keyword>repeat</keyword>
+          <keyword>replace</keyword>
+          <keyword>search</keyword>
+          <keyword>slice</keyword>
+          <keyword>split</keyword>
+          <keyword>startsWith</keyword>
+          <keyword>substring</keyword>
+          <keyword>toLocaleLowerCase</keyword>
+          <keyword>toLocaleUpperCase</keyword>
+          <keyword>toLowerCase</keyword>
+          <keyword>toUpperCase</keyword>
+          <keyword>trimEnd</keyword> <!-- ES2019 -->
+          <keyword>trimStart</keyword> <!-- ES2019 -->
+          <keyword>trim</keyword>
+        </context> <!-- /_string-methods -->
+
+        <context id="_symbol-properties" style-ref="js:built-in-property">
+          <keyword>asyncIterator</keyword> <!-- ES2018 -->
+          <keyword>description</keyword> <!-- ES2019 -->
+          <keyword>hasInstance</keyword>
+          <keyword>isConcatSpreadable</keyword>
+          <keyword>iterator</keyword>
+          <keyword>matchAll</keyword>
+          <keyword>match</keyword>
+          <keyword>replace</keyword>
+          <keyword>search</keyword>
+          <keyword>species</keyword>
+          <keyword>split</keyword>
+          <keyword>toPrimitive</keyword>
+          <keyword>toStringTag</keyword>
+          <keyword>unscopables</keyword>
+        </context> <!-- /_symbol-properties -->
+
+        <context id="_symbol-methods" style-ref="js:built-in-method">
+          <keyword>for</keyword>
+          <keyword>keyFor</keyword>
+        </context> <!-- /_symbol-methods -->
+
+        <!-- TypedArray is one of these objects:
+             * Float32Array
+             * Float64Array
+             * Int16Array
+             * Int32Array
+             * Int8Array
+             * Uint16Array
+             * Uint32Array
+             * Uint8Array
+             * Uint8ClampedArray
+        -->
+        <context id="_typedarray-properties" style-ref="js:built-in-property">
+          <keyword>buffer</keyword>
+          <keyword>byteLength</keyword>
+          <keyword>byteOffset</keyword>
+          <keyword>BYTES_PER_ELEMENT</keyword>
+          <keyword>length</keyword>
+          <keyword>name</keyword>
+        </context> <!-- /_typedarray-properties -->
+
+        <context id="_typedarray-methods" style-ref="js:built-in-method">
+          <keyword>copyWithin</keyword>
+          <keyword>entries</keyword>
+          <keyword>every</keyword>
+          <keyword>fill</keyword>
+          <keyword>filter</keyword>
+          <keyword>findIndex</keyword>
+          <keyword>find</keyword>
+          <keyword>forEach</keyword>
+          <keyword>from</keyword>
+          <keyword>indexOf</keyword>
+          <keyword>join</keyword>
+          <keyword>keys</keyword>
+          <keyword>lastIndexOf</keyword>
+          <keyword>map</keyword>
+          <keyword>of</keyword>
+          <keyword>reduceRight</keyword>
+          <keyword>reduce</keyword>
+          <keyword>reverse</keyword>
+          <keyword>set</keyword>
+          <keyword>slice</keyword>
+          <keyword>some</keyword>
+          <keyword>sort</keyword>
+          <keyword>subarray</keyword>
+          <keyword>values</keyword>
+        </context> <!-- /_typedarray-methods -->
+
+        <context id="_weakmap-methods" style-ref="js:built-in-method">
+          <keyword>delete</keyword>
+          <keyword>get</keyword>
+          <keyword>has</keyword>
+          <keyword>set</keyword>
+        </context> <!-- /_weakmap-methods -->
+
+        <context id="_weakset-methods" style-ref="js:built-in-method">
+          <keyword>add</keyword>
+          <keyword>delete</keyword>
+          <keyword>has</keyword>
+        </context> <!-- /_weakset-methods -->
+
+        <context id="_webassembly-methods" style-ref="js:built-in-method">
+          <keyword>compileStreaming</keyword>
+          <keyword>compile</keyword>
+          <keyword>instantiateStreaming</keyword>
+          <keyword>instantiate</keyword>
+          <keyword>validate</keyword>
+        </context> <!-- /_webassembly-methods -->
+
+        <context id="_webassembly-instance-properties" style-ref="js:built-in-property">
+          <keyword>exports</keyword>
+        </context> <!-- /_webassembly-instance-properties -->
+
+        <context id="_webassembly-global-properties" style-ref="js:built-in-property">
+          <keyword>value</keyword>
+        </context> <!-- /_webassembly-global-properties -->
+
+        <context id="_webassembly-module-methods" style-ref="js:built-in-method">
+          <keyword>customSections</keyword>
+          <keyword>exports</keyword>
+          <keyword>imports</keyword>
+        </context> <!-- /_webassembly-module-methods -->
+
+        <context id="_webassembly-memory-properties" style-ref="js:built-in-property">
+          <keyword>buffer</keyword>
+        </context> <!-- /_webassembly-memory-properties -->
+
+        <context id="_webassembly-memory-methods" style-ref="js:built-in-method">
+          <keyword>grow</keyword>
+        </context> <!-- /_webassembly-memory-methods -->
+
+        <context id="_webassembly-table-properties" style-ref="js:built-in-property">
+          <keyword>length</keyword>
+        </context> <!-- /_webassembly-table-properties -->
+
+        <context id="_webassembly-table-methods" style-ref="js:built-in-method">
+          <keyword>get</keyword>
+          <keyword>grow</keyword>
+          <keyword>set</keyword>
+        </context> <!-- /_webassembly-table-methods -->
+
+      </include>
+    </context> <!-- /properties-methods -->
+
+  </definitions>
+</language>
diff --git a/data/language-specs/javascript.lang b/data/language-specs/javascript.lang
index d9627cbf..ca904861 100644
--- a/data/language-specs/javascript.lang
+++ b/data/language-specs/javascript.lang
@@ -8,7 +8,7 @@
  Copyright (C) 2005 Stef Walter (formerly Nate Nielsen) <stef memberwebs com>
  Copyright (C) 2005-2007 Marco Barisione <barisione gmail com>
  Copyright (C) 2005-2007 Emanuele Aina
- Copyright (C) 2018 Jeffery To <jeffery to gmail com>
+ Copyright (C) 2019 Jeffery To <jeffery to gmail com>
 
  GtkSourceView is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
@@ -34,838 +34,704 @@
   </metadata>
 
   <styles>
-    <style id="escape"          name="Escaped Character"        map-to="def:special-char"/>
-    <style id="decimal"         name="Decimal Number"           map-to="def:decimal"/>
-    <style id="base-n-number"   name="Base-N Number"            map-to="def:base-n-integer"/>
-    <style id="big-integer"     name="Big Integer"              map-to="def:number"/>
-    <style id="null-value"      name="Null Value"               map-to="def:special-constant"/>
-    <style id="undefined-value" name="Undefined Value"          map-to="def:special-constant"/>
-    <style id="boolean"         name="Boolean Value"            map-to="def:boolean"/>
-    <style id="keyword"         name="Keyword"                  map-to="def:keyword"/>
-    <style id="type"            name="Data Type"                map-to="def:type"/>
-    <style id="function"        name="Function"                 map-to="def:builtin"/>
-    <style id="properties"      name="Properties"               map-to="def:statement"/>
-    <style id="constructors"    name="Constructors"             map-to="def:type"/>
-    <style id="future-words"    name="Future Reserved Keywords" map-to="def:error"/>
-    <style id="string"          name="String"                   map-to="def:string"/>
-    <style id="regex"           name="Regular Expression"       map-to="def:string"/>
+
+    <!-- General -->
+    <style id="error"                 name="Error"                  map-to="def:error"/>
+    <style id="keyword"               name="Keyword"                map-to="def:keyword"/>
+    <style id="reserved-word"         name="Reserved word"          map-to="def:reserved"/>
+    <style id="directive"             name="Directive"              map-to="def:preprocessor"/>
+
+    <!-- Literals -->
+    <style id="null-value"            name="Null value"             map-to="def:special-constant"/>
+    <style id="boolean"               name="Boolean value"          map-to="def:boolean"/>
+    <style id="decimal"               name="Decimal number"         map-to="def:decimal"/>
+    <style id="base-n-integer"        name="Base-n integer"         map-to="def:base-n-integer"/>
+    <style id="binary-integer"        name="Binary integer"         map-to="js:base-n-integer"/>
+    <style id="octal-integer"         name="Octal integer"          map-to="js:base-n-integer"/>
+    <style id="hex-integer"           name="Hexadecimal integer"    map-to="js:base-n-integer"/>
+    <style id="escape"                name="Escaped character"      map-to="def:special-char"/>
+    <style id="string"                name="String"                 map-to="def:string"/>
+    <style id="included-file"         name="Included file"          map-to="js:string"/>
+    <style id="regex"                 name="Regular expression"     map-to="def:string"/>
+    <style id="regex-class"           name="RE character class"     map-to="def:special-char"/>
+    <style id="regex-group"           name="RE group name"          map-to="def:identifier"/>
+    <style id="regex-flag"            name="RE flag"                map-to="def:special-constant"/>
+    <style id="template-literal"      name="Template literal"       map-to="js:string"/>
+    <style id="template-placeholder"  name="Template placeholder"   map-to="def:preprocessor"/>
+
+    <!-- Built-in values -->
+    <style id="built-in-value"        name="Built-in value"         map-to="def:special-constant"/>
+    <style id="built-in-function"     name="Built-in function"      map-to="def:builtin"/>
+    <style id="built-in-constructor"  name="Built-in constructor"   map-to="def:type"/>
+    <style id="built-in-object"       name="Built-in object"        map-to="def:builtin"/>
+    <style id="built-in-property"     name="Built-in property"      map-to="def:statement"/>
+    <style id="built-in-method"       name="Built-in method"        map-to="js:built-in-function"/>
+
+    <!-- The following are for debugging use -->
+
+    <!-- General -->
+    <style id="identifier"                    name="Identifier"/>
+    <style id="rest-syntax"                   name="Rest syntax"/>
+    <style id="spread-syntax"                 name="Spread syntax"/>
+    <style id="array-destructuring"           name="Array destructuring"/>
+    <style id="object-destructuring"          name="Object destructuring"/>
+
+    <!-- Literals -->
+    <style id="array-literal"                 name="Array literal"/>
+    <style id="object-literal"                name="Object literal"/>
+
+    <!-- Functions and classes -->
+    <style id="function-expression"           name="Function expression"/>
+    <style id="class-expression"              name="Class expression"/>
+
+    <!-- Expressions -->
+    <style id="grouping"                      name="Grouping"/>
+    <style id="grouping-operator"             name="Grouping operator"/>
+    <style id="increment-decrement-operator"  name="Increment decrement operator"/>
+    <style id="unary-operator"                name="Unary operator"/>
+    <style id="binary-operator"               name="Binary operator"/>
+    <style id="comma-operator"                name="Comma operator"/>
+    <style id="ternary-operator"              name="Ternary operator"/>
+    <style id="expression"                    name="Expression"/>
+
+    <!-- Statements -->
+    <style id="block-statement"               name="Block statement"/>
+    <style id="break-statement"               name="Break statement"/>
+    <style id="continue-statement"            name="Continue statement"/>
+    <style id="debugger-statement"            name="Debugger statement"/>
+    <style id="expression-statement"          name="Expression statement"/>
+    <style id="for-statement"                 name="For statement"/>
+    <style id="if-statement"                  name="If statement"/>
+    <style id="else-statement"                name="Else statement"/>
+    <style id="label-statement"               name="Label statement"/>
+    <style id="return-statement"              name="Return statement"/>
+    <style id="switch-statement"              name="Switch statement"/>
+    <style id="throw-statement"               name="Throw statement"/>
+    <style id="try-catch-statement"           name="Try catch statement"/>
+    <style id="variable-declaration"          name="Variable declaration"/>
+    <style id="while-statement"               name="While statement"/>
+    <style id="with-statement"                name="With statement"/>
+
+    <!-- Modules -->
+    <style id="export-import-declaration"     name="Export import declaration"/>
+
   </styles>
 
+  <keyword-char-class>[a-zA-Z0-9_$]</keyword-char-class>
+
   <definitions>
 
-    <define-regex id="is-member">(?&lt;=[^.]\.)</define-regex>
+    <!--
+         "Tags" (e.g. <Script>) or section numbers (e.g. 13.2 Block)
+         inside comments refer to relevant productions or sections from
+         the ECMAScript Language Specification, respectively.
+
+         Current draft: https://tc39.es/ecma262/
+
+         Supported level: ES2019
+
+         Supported ES2020 proposals:
+         * String.prototype.matchAll
+         * import()
+         * BigInt
+         * Promise.allSettled
+         * globalThis
+
+         Features from Annex B of the spec are not highlighted to
+         discourage their use, except:
+         * Legacy octal number literals: Highlighted as errors to
+           reduce accidental usage
+         * Legacy octal escape sequences: Highlighted as normal escapes
+           as they are inside strings; the string and error styles may
+           conflict with each other
+
+         Not supported yet:
+         * SharedArrayBuffer and Atomics (ES2017): Browser support is
+           minimal / disabled to mitigate speculative side-channel
+           attacks (Meltdown / Spectre)
+         * RegExp lookbehind assertions (ES2018): Lookahead assertions
+           are not highlighted either
+
+
+         Also supported:
+         * ECMAScript Internationalization API
+           Current draft: https://tc39.es/ecma402/
+           Supported level: 2018
+         * WebAssembly JavaScript Interface
+           Editor's draft: https://webassembly.github.io/spec/js-api/
+           Supported level: Candidate Recommendation, 18 July 2019
+         * WebAssembly Web API
+           Editor's draft: https://webassembly.github.io/spec/web-api/
+           Supported level: Candidate Recommendation, 18 July 2019
+
+
+         Naming convention for contexts:
+         * Prefixes:
+           * "_" (underscore): "Private" to one or more "public"
+             contexts (can still be overriden by child languages)
+           * "ordered-": Wrapper for another context so that it can only
+             match in a referenced position (has once-only="true")
+           * "last-": An "ordered-" wrapper with end-parent="true",
+              which will end the parent under all circumstances
+           * "choice-": One option in a group where only one can be
+              selected (has end-parent="true")
+         * Suffixes:
+           * Plural: Can match multiple times
+           * Singular: Has once-only="true"
+           * "-end-parent": Has end-parent="true"
+           * "-no-extend-parent": Has extend-parent="false"
+           * "-content": Holds children of container contexts
+
+         End-parent contexts are container contexts to avoid the
+         end-parent bug:
+         https://gitlab.gnome.org/GNOME/gtksourceview/issues/14
+    -->
+
+
+    <!-- # General -->
+
+    <!-- <UnicodeEscapeSequence> -->
+    <define-regex id="unicode-escape" extended="true">
+      (?: \\u (?: [0-9a-fA-F]{4} | \{ [0-9a-fA-F]{1,} \} ) )
+    </define-regex> <!-- /unicode-escape -->
+
+    <!-- <IdentifierStart> -->
+    <!-- should include all Unicode ID_Start code points -->
+    <define-regex id="identifier-start" extended="true">
+      (?: [a-zA-Z_$] | \%{unicode-escape} )
+    </define-regex> <!-- /identifier-start -->
 
-    <!--regex-->
-    <define-regex id="regex-opts">[gim]*</define-regex>
+    <!-- for lookbehinds -->
+    <define-regex id="identifier-char">[a-zA-Z0-9_$]</define-regex>
+
+    <!-- <IdentifierPart> -->
+    <!-- should include all Unicode ID_Continue code points,
+         ZWNJ / ZWJ -->
+    <define-regex id="identifier-part" extended="true">
+      (?: \%{identifier-char} | \%{unicode-escape} )
+    </define-regex> <!-- /identifier-part -->
+
+    <!-- <IdentifierName> -->
+    <define-regex id="identifier" extended="true">
+      (?: \%{identifier-start} \%{identifier-part}* )
+    </define-regex> <!-- /identifier-->
+
+    <define-regex id="before-next-token">(?=\S)</define-regex>
+
+    <define-regex id="statement-end" extended="true">
+      (?: ; | \%{before-next-token} )
+    </define-regex> <!-- /statement-end -->
+
+    <define-regex id="statement-end-or-end-of-line" extended="true">
+      (?: \%{statement-end} | $ )
+    </define-regex> <!-- /statement-end-or-end-of-line -->
+
+    <!-- wherever this is used, the highlighting will be more brittle,
+         because comments can span multiple lines -->
+    <define-regex id="optional-whitespace-or-comments" extended="true">
+      (?&gt; (?: \s+ | /\*.*?\*/ )* )
+    </define-regex> <!-- /optional-whitespace-or-comments -->
+
+    <define-regex id="generator-modifier">\*</define-regex>
+
+    <!-- "unknown id" errors can occur when using a regex defined in one
+          component file in another component file
+          so we move the definitions of these regexes here
+    -->
 
-    <!--contexts NOT used in the main context-->
-    <context id="escape" style-ref="escape">
-      <match extended="true">
-        \\
+    <define-regex id="import-function-keyword-suffix" extended="true">
+      (?:
+        \%{optional-whitespace-or-comments}
+        \(
+      )
+    </define-regex> <!-- /import-function-keyword-suffix -->
+
+    <define-regex id="new-target-object-keyword-suffix" extended="true">
+      (?:
+        \%{optional-whitespace-or-comments}
+        \.
+        \%{optional-whitespace-or-comments}
+        target \%]
+      )
+    </define-regex> <!-- /new-target-object-keyword-suffix -->
+
+    <!-- async function (ES2017)
+         no line terminator allowed between "async" and "function" -->
+    <define-regex id="function-expression-keyword" extended="true">
+      (?:
         (?:
-          (?: [1-7][0-7]{0,2} | [0-7]{2,3} ) |  # octal escape (\0 is the null character, not octal)
-          x[0-9a-fA-F]{2} |                     # hexadecimal escape
-          u[0-9a-fA-F]{4} |                     # unicode escape
-          u\{[0-9a-fA-F]{1,}\} |                # unicode code point escape
-          .                                     # single character escape
-        )
-      </match>
-    </context>
-
-    <!-- only valid in regular expressions -->
-    <context id="control-escape" style-ref="escape">
-      <match>\\c[a-zA-Z]</match>
-    </context>
-
-    <context id="regex-bracketed" style-ref="escape" style-inside="true">
+          \%[ async \%]
+          \%{optional-whitespace-or-comments}
+        )?
+        \%[ function \%]
+      )
+    </define-regex> <!-- /function-expression-keyword -->
+
+    <define-regex id="class-expression-keyword" extended="true">
+      (?: \%[ class \%] )
+    </define-regex> <!-- /class-expression-keyword -->
+
+    <!-- ## Embedded lang hooks
+         a placeholder context where an embedding language (e.g. html)
+         can <replace>
+    -->
+
+    <context id="embedded-lang-hooks"/>
+
+    <!-- ## Comments -->
+
+    <context id="_in-comment" class-disabled="no-spell-check">
+      <include>
+        <context ref="embedded-lang-hooks"/>
+        <context ref="def:in-comment" original="true"/>
+      </include>
+    </context> <!-- /_in-comment -->
+
+    <replace id="def:in-comment" ref="_in-comment"/>
+
+    <!-- includes def:in-comment instead of def:in-line-comment,
+         because line continuations are not allowed inside single-line
+         comments -->
+    <context id="_c-like-comment" style-ref="def:comment" end-at-line-end="true" 
class-disabled="no-spell-check" class="comment">
+      <start>//</start>
+      <include>
+        <context ref="def:in-comment"/>
+      </include>
+    </context> <!-- /_c-like-comment -->
+
+    <context id="_c-like-comment-no-extend-parent" style-ref="def:comment" end-at-line-end="true" 
class-disabled="no-spell-check" class="comment" extend-parent="false">
+      <start>//</start>
+      <include>
+        <context ref="def:in-comment"/>
+      </include>
+    </context> <!-- /_c-like-comment-no-extend-parent -->
+
+    <context id="_c-like-comment-multiline-no-extend-parent" style-ref="def:comment" 
class-disabled="no-spell-check" class="comment" extend-parent="false">
+      <start>/\*</start>
+      <end>\*/</end>
+      <include>
+        <context ref="def:in-comment"/>
+      </include>
+    </context> <!-- /_c-like-comment-multiline-no-extend-parent -->
+
+    <context id="comments">
+      <include>
+        <context ref="_c-like-comment"/>
+        <context ref="def:c-like-comment-multiline"/>
+        <context ref="def:c-like-close-comment-outside-comment"/>
+      </include>
+    </context> <!-- /comments -->
+
+    <!-- for statements that cannot contain line terminators -->
+    <context id="comments-no-extend-parent">
+      <include>
+        <context ref="_c-like-comment-no-extend-parent"/>
+        <context ref="_c-like-comment-multiline-no-extend-parent"/>
+        <context ref="def:c-like-close-comment-outside-comment"/>
+      </include>
+    </context> <!-- /comments-no-extend-parent -->
+
+    <!-- ## Identifiers -->
+
+    <!-- <ReservedWord> -->
+    <context id="_reserved-words">
+      <include>
+
+        <!-- <Keyword> -->
+        <context id="_keywords" style-ref="reserved-word">
+          <keyword>await</keyword> <!-- ES2017 -->
+          <keyword>break</keyword>
+          <keyword>case</keyword>
+          <keyword>catch</keyword>
+          <keyword>class</keyword>
+          <keyword>const</keyword>
+          <keyword>continue</keyword>
+          <keyword>debugger</keyword>
+          <keyword>default</keyword>
+          <keyword>delete</keyword>
+          <keyword>do</keyword>
+          <keyword>else</keyword>
+          <keyword>export</keyword>
+          <keyword>extends</keyword>
+          <keyword>finally</keyword>
+          <keyword>for</keyword>
+          <keyword>function</keyword>
+          <keyword>if</keyword>
+          <keyword>import</keyword>
+          <keyword>instanceof</keyword>
+          <keyword>in</keyword>
+          <keyword>new</keyword>
+          <keyword>return</keyword>
+          <keyword>super</keyword>
+          <keyword>switch</keyword>
+          <keyword>this</keyword>
+          <keyword>throw</keyword>
+          <keyword>try</keyword>
+          <keyword>typeof</keyword>
+          <keyword>var</keyword>
+          <keyword>void</keyword>
+          <keyword>while</keyword>
+          <keyword>with</keyword>
+          <keyword>yield</keyword> <!-- allowed as a variable name in non-strict mode -->
+        </context> <!-- /_keywords -->
+
+        <!-- "treated as reserved words through static semantic
+             restrictions"
+             https://tc39.github.io/ecma262/#sec-keywords -->
+        <context id="_strict-mode-keywords" style-ref="reserved-word">
+          <keyword>let</keyword>
+          <keyword>static</keyword>
+        </context> <!-- /_strict-mode-keywords -->
+
+        <!-- <FutureReservedWord> -->
+        <context id="_future-reserved-words" style-ref="reserved-word">
+          <keyword>enum</keyword>
+        </context> <!-- /_future-reserved-words -->
+
+        <!-- usage in strict mode "restricted using static semantic
+             restrictions"
+             https://tc39.github.io/ecma262/#sec-future-reserved-words -->
+        <context id="_strict-mode-future-reserved-words" style-ref="reserved-word">
+          <keyword>implements</keyword>
+          <keyword>interface</keyword>
+          <keyword>package</keyword>
+          <keyword>private</keyword>
+          <keyword>protected</keyword>
+          <keyword>public</keyword>
+        </context> <!-- /_strict-mode-future-reserved-words -->
+
+        <context ref="js-lit:null-value" style-ref="reserved-word"/>
+        <context ref="js-lit:boolean" style-ref="reserved-word"/>
+      </include>
+    </context> <!-- /_reserved-words -->
+
+    <context id="_identifier-names" style-ref="identifier">
+      <match>\%{identifier}</match>
+    </context> <!-- /_identifier-names -->
+
+    <context id="choice-identifier-name" end-parent="true">
+      <start>(?=\%{identifier-start})</start>
+      <end>\%{def:always-match}</end>
+      <include>
+        <!-- no embedded-lang-hooks here -->
+        <!-- no comments here -->
+        <!-- do not extend the context by matching comments or
+             embedded-lang-hooks, which may lead to multiple identifiers -->
+
+        <context id="_identifier-name-content">
+          <include>
+            <context ref="_identifier-names"/>
+          </include>
+        </context> <!-- /_identifier-name-content -->
+
+      </include>
+    </context> <!-- /choice-identifier-name -->
+
+    <context id="_identifier-content">
+      <include>
+        <context ref="_reserved-words"/>
+        <context ref="_identifier-names"/>
+      </include>
+    </context> <!-- /_identifier-content -->
+
+    <context id="identifier" once-only="true">
+      <start>(?=\%{identifier-start})</start>
+      <end>\%{def:always-match}</end>
+      <include>
+        <!-- no embedded-lang-hooks here -->
+        <!-- no comments here -->
+        <!-- do not extend the context by matching comments or
+             embedded-lang-hooks, which may lead to multiple identifiers -->
+        <context ref="_identifier-content"/>
+      </include>
+    </context> <!-- /identifier -->
+
+    <context id="choice-identifier" end-parent="true">
+      <start>(?=\%{identifier-start})</start>
+      <end>\%{def:always-match}</end>
+      <include>
+        <!-- no embedded-lang-hooks here -->
+        <!-- no comments here -->
+        <!-- do not extend the context by matching comments or
+             embedded-lang-hooks, which may lead to multiple identifiers -->
+        <context ref="_identifier-content"/>
+      </include>
+    </context> <!-- /choice-identifier -->
+
+    <context id="ordered-identifier" once-only="true">
+      <start>\%{before-next-token}</start>
+      <end>\%{before-next-token}</end>
+      <include>
+        <context ref="identifier"/>
+      </include>
+    </context> <!-- /ordered-identifier -->
+
+    <!-- ## Default value assignment -->
+
+    <!-- <Initializer> -->
+    <context id="_default-value-assignment" once-only="true">
+      <start>=</start>
+      <end>\%{before-next-token}</end>
+      <include>
+        <context ref="embedded-lang-hooks"/>
+        <context ref="comments"/>
+
+        <context id="_default-value-assignment-content">
+          <include>
+            <context ref="js-expr:expression-without-comma"/>
+          </include>
+        </context> <!-- /_default-value-assignment-content -->
+
+      </include>
+    </context> <!-- /_default-value-assignment -->
+
+    <context id="ordered-default-value-assignment" once-only="true">
+      <start>\%{before-next-token}</start>
+      <end>\%{before-next-token}</end>
+      <include>
+        <context ref="_default-value-assignment"/>
+      </include>
+    </context> <!-- /ordered-default-value-assignment -->
+
+    <!-- ## Misc syntax -->
+
+    <context id="_keyword-dot" style-ref="keyword" once-only="true">
+      <match>\.</match>
+    </context> <!-- /_keyword-dot -->
+
+    <context id="ordered-keyword-dot" once-only="true">
+      <start>\%{before-next-token}</start>
+      <end>\%{before-next-token}</end>
+      <include>
+        <context ref="_keyword-dot"/>
+      </include>
+    </context> <!-- /ordered-keyword-dot -->
+
+    <context id="_rest-syntax" style-ref="rest-syntax" once-only="true">
+      <match>\.\.\.</match>
+    </context> <!-- /_rest-syntax -->
+
+    <context id="ordered-rest-syntax" once-only="true">
+      <start>\%{before-next-token}</start>
+      <end>\%{before-next-token}</end>
+      <include>
+        <context ref="_rest-syntax"/>
+      </include>
+    </context> <!-- /ordered-rest-syntax -->
+
+    <context id="_spread-syntax" style-ref="spread-syntax" once-only="true">
+      <match>\.\.\.</match>
+    </context> <!-- /_spread-syntax -->
+
+    <context id="ordered-spread-syntax" once-only="true">
+      <start>\%{before-next-token}</start>
+      <end>\%{before-next-token}</end>
+      <include>
+        <context ref="_spread-syntax"/>
+      </include>
+    </context> <!-- /ordered-spread-syntax -->
+
+    <context id="generator-modifier" once-only="true">
+      <match>\%{generator-modifier}</match>
+    </context> <!-- /generator-modifier -->
+
+    <context id="ordered-generator-modifier" once-only="true">
+      <start>\%{before-next-token}</start>
+      <end>\%{before-next-token}</end>
+      <include>
+        <context ref="generator-modifier"/>
+      </include>
+    </context> <!-- /ordered-generator-modifier -->
+
+
+    <!-- # Assignment target -->
+
+    <!-- ## Array destructuring
+
+         [ a, b, ...rest ] = [ 1, 2, 3, 4 ]
+         [ a = 1, b = 2, c = 3 ] = [ 11, 12 ]
+    -->
+
+    <context id="_array-destructuring-element-content">
+      <include>
+        <context ref="ordered-rest-syntax"/>
+        <context ref="ordered-assignment-target"/>
+        <context ref="ordered-default-value-assignment"/>
+      </include>
+    </context> <!-- /_array-destructuring-element-content -->
+
+    <!-- <ArrayBindingPattern> -->
+    <context id="_choice-array-destructuring" style-ref="array-destructuring" end-parent="true">
       <start>\[</start>
       <end>]</end>
       <include>
-        <context ref="control-escape"/>
-        <context ref="escape"/>
+        <context ref="embedded-lang-hooks"/>
+        <context ref="comments"/>
+
+        <context id="_array-destructuring-content">
+          <include>
+
+            <context id="_array-destructuring-first-element" once-only="true">
+              <start>\%{before-next-token}</start>
+              <end>\%{before-next-token}</end>
+              <include>
+                <context ref="embedded-lang-hooks"/>
+                <context ref="comments"/>
+                <context ref="_array-destructuring-element-content"/>
+              </include>
+            </context> <!-- /_array-destructuring-first-element -->
+
+            <context id="_array-destructuring-elements">
+              <start>,</start>
+              <end>\%{before-next-token}</end>
+              <include>
+                <context ref="embedded-lang-hooks"/>
+                <context ref="comments"/>
+                <context ref="_array-destructuring-element-content"/>
+              </include>
+            </context> <!-- /_array-destructuring-elements -->
+
+          </include>
+        </context> <!-- /_array-destructuring-content -->
+
       </include>
-    </context>
+    </context> <!-- /_choice-array-destructuring -->
 
-    <!--contexts used in the main context-->
-    <context id="string" style-ref="string" end-at-line-end="true" class="string" 
class-disabled="no-spell-check">
-      <start>"</start>
-      <end>"</end>
+    <!-- ## Object destructuring
+
+         { a, y: b, ...rest } = { a: 1, y: 2, i: 3, y: 4 }
+         { a = 1, y: b = 2, c = 3 } = { a: 11, y: 12 }
+    -->
+
+    <context id="_object-destructuring-assignment-target" once-only="true">
+      <start>:</start>
+      <end>\%{before-next-token}</end>
       <include>
-        <context ref="escape"/>
-        <context ref="def:line-continue"/>
+        <context ref="embedded-lang-hooks"/>
+        <context ref="comments"/>
+
+        <context id="_object-destructuring-assignment-target-content">
+          <include>
+            <context ref="ordered-assignment-target"/>
+          </include>
+        </context> <!-- /_object-destructuring-assignment-target-content -->
+
       </include>
-    </context>
+    </context> <!-- /_object-destructuring-assignment-target -->
 
-    <context id="single-quoted-string" style-ref="string" end-at-line-end="true" class="string" 
class-disabled="no-spell-check">
-      <start>'</start>
-      <end>'</end>
+    <context id="_ordered-object-destructuring-assignment-target" once-only="true">
+      <start>\%{before-next-token}</start>
+      <end>\%{before-next-token}</end>
       <include>
-        <context ref="escape"/>
-        <context ref="def:line-continue"/>
+        <context ref="_object-destructuring-assignment-target"/>
       </include>
-    </context>
-
-    <context id="regex-simple" style-ref="regex">
-      <start extended="true">
-        ((?&lt;=([(]|\s))|^)
-        \/
-        (?=
-          ([^/\\]*(\\.))*
-          [^/]*
-          \/
-          \%{regex-opts}
-          \s*
-          ([),;.\/\]:}]|$)
-        )</start>
-      <end>\/\%{regex-opts}</end>
+    </context> <!-- /_ordered-object-destructuring-assignment-target -->
+
+    <context id="_object-destructuring-property-content">
       <include>
-        <context ref="control-escape"/>
-        <context ref="escape"/>
-        <context ref="regex-bracketed"/>
+        <context ref="ordered-rest-syntax"/> <!-- ES2018 -->
+        <context ref="js-lit:ordered-property-name"/>
+        <context ref="_ordered-object-destructuring-assignment-target"/>
+        <context ref="ordered-default-value-assignment"/>
       </include>
-    </context>
-
-    <context id="decimal" style-ref="decimal">
-      <match extended="true">
-        (?&lt;! [\w\.] )
-        (?&gt;
-          ( [1-9][0-9]* | 0 ) ( \. [0-9]* )? |
-          \. [0-9]+
-        )
-        (?&gt; [eE] [+-]? [0-9]+ )?
-        (?! [\w\.] )
-      </match>
-    </context>
-
-    <context id="binary-integer" style-ref="base-n-number">
-      <match extended="true">
-        (?&lt;![\w\.]) (?&gt;0 [bB] [01]+) (?![\w\.])
-      </match>
-    </context>
-
-    <context id="octal-integer" style-ref="base-n-number">
-      <match extended="true">
-        (?&lt;![\w\.]) (?&gt;0 [oO] [0-7]+) (?![\w\.])
-      </match>
-    </context>
-
-    <context id="hex-integer" style-ref="base-n-number">
-      <match extended="true">
-        (?&lt;![\w\.]) (?&gt;0 [xX] [0-9a-fA-F]+) (?![\w\.])
-      </match>
-    </context>
-
-    <context id="big-integer" style-ref="big-integer">
-      <match extended="true">
-        (?&lt;![\w\.]) (?&gt;( [1-9][0-9]* | 0 ) n) (?![\w\.])
-      </match>
-    </context>
-
-    <!-- deprecated -->
-    <context id="legacy-octal-integer" style-ref="base-n-number">
-      <match extended="true">
-        (?&lt;![\w\.]) (?&gt;0 [0-7]+) (?![\w\.])
-      </match>
-    </context>
+    </context> <!-- /_object-destructuring-property-content -->
 
-    <!--
-        There was a long discussion on ##javascript on freenode between
-        'katspaugh', 'joo' and 'prog_' on whether 'undefined' should be
-        highlighted on not, specialy as a constant. The conclusion was "It can't
-        be highlighted as a constant literal value, because it can be an
-        identifier (of a variable value) but leave it be as it is. Let tradition
-        and convention obscure the details."
-    -->
-    <context id="undefined-value" style-ref="undefined-value">
-      <keyword>undefined</keyword>
-    </context>
-
-    <context id="null-value" style-ref="null-value">
-      <keyword>null</keyword>
-    </context>
-
-    <context id="boolean" style-ref="boolean">
-      <keyword>false</keyword>
-      <keyword>true</keyword>
-    </context>
-
-    <context id="keywords" style-ref="keyword">
-      <keyword>arguments</keyword>
-      <keyword>async</keyword>
-      <keyword>as</keyword>
-      <keyword>await</keyword>
-      <keyword>break</keyword>
-      <keyword>case</keyword>
-      <keyword>catch</keyword>
-      <keyword>class</keyword>
-      <keyword>const</keyword>
-      <keyword>constructor</keyword>
-      <keyword>continue</keyword>
-      <keyword>debugger</keyword>
-      <keyword>default</keyword>
-      <keyword>delete</keyword>
-      <keyword>do</keyword>
-      <keyword>else</keyword>
-      <keyword>export</keyword>
-      <keyword>extends</keyword>
-      <keyword>finally</keyword>
-      <keyword>for</keyword>
-      <keyword>from</keyword>
-      <keyword>function</keyword>
-      <keyword>get</keyword>
-      <keyword>if</keyword>
-      <keyword>import</keyword>
-      <keyword>instanceof</keyword>
-      <keyword>in</keyword>
-      <keyword>let</keyword>
-      <keyword>new</keyword>
-      <keyword>of</keyword>
-      <keyword>return</keyword>
-      <keyword>set</keyword>
-      <keyword>static</keyword>
-      <keyword>super</keyword>
-      <keyword>switch</keyword>
-      <keyword>this</keyword>
-      <keyword>throw</keyword>
-      <keyword>try</keyword>
-      <keyword>typeof</keyword>
-      <keyword>var</keyword>
-      <keyword>void</keyword>
-      <keyword>while</keyword>
-      <keyword>with</keyword>
-      <keyword>yield</keyword>
-    </context>
-
-    <context id="types" style-ref="type">
-      <keyword>Infinity</keyword>
-      <keyword>NaN</keyword>
-    </context>
-
-    <context id="global-functions" style-ref="function">
-      <keyword>decodeURIComponent</keyword>
-      <keyword>decodeURI</keyword>
-      <keyword>encodeURIComponent</keyword>
-      <keyword>encodeURI</keyword>
-      <keyword>escape</keyword> <!-- deprecated -->
-      <keyword>eval</keyword>
-      <keyword>isFinite</keyword>
-      <keyword>isNaN</keyword>
-      <keyword>parseFloat</keyword>
-      <keyword>parseInt</keyword>
-      <keyword>unescape</keyword> <!-- deprecated -->
-    </context>
-
-    <context id="array-properties" style-ref="properties">
-      <prefix>\%{is-member}</prefix>
-      <keyword>length</keyword>
-    </context>
-
-    <context id="array-methods" style-ref="function">
-      <prefix>\%{is-member}</prefix>
-      <keyword>concat</keyword>
-      <keyword>copyWithin</keyword>
-      <keyword>entries</keyword>
-      <keyword>every</keyword>
-      <keyword>fill</keyword>
-      <keyword>filter</keyword>
-      <keyword>findIndex</keyword>
-      <keyword>find</keyword>
-      <keyword>forEach</keyword>
-      <keyword>from</keyword>
-      <keyword>indexOf</keyword>
-      <keyword>isArray</keyword>
-      <keyword>join</keyword>
-      <keyword>keys</keyword>
-      <keyword>lastIndexOf</keyword>
-      <keyword>map</keyword>
-      <keyword>of</keyword>
-      <keyword>pop</keyword>
-      <keyword>push</keyword>
-      <keyword>reduceRight</keyword>
-      <keyword>reduce</keyword>
-      <keyword>reverse</keyword>
-      <keyword>shift</keyword>
-      <keyword>slice</keyword>
-      <keyword>some</keyword>
-      <keyword>sort</keyword>
-      <keyword>unshift</keyword>
-      <keyword>values</keyword>
-    </context>
-
-    <context id="arraybuffer-properties" style-ref="properties">
-      <prefix>\%{is-member}</prefix>
-      <keyword>byteLength</keyword>
-    </context>
-
-    <context id="arraybuffer-methods" style-ref="function">
-      <prefix>\%{is-member}</prefix>
-      <keyword>isView</keyword>
-      <keyword>slice</keyword>
-    </context>
-
-    <context id="dataview-properties" style-ref="properties">
-      <prefix>\%{is-member}</prefix>
-      <keyword>buffer</keyword>
-      <keyword>byteLength</keyword>
-      <keyword>byteOffset</keyword>
-    </context>
-
-    <context id="dataview-methods" style-ref="function">
-      <prefix>\%{is-member}</prefix>
-      <keyword>getFloat32</keyword>
-      <keyword>getFloat64</keyword>
-      <keyword>getInt16</keyword>
-      <keyword>getInt32</keyword>
-      <keyword>getInt8</keyword>
-      <keyword>getUint16</keyword>
-      <keyword>getUint32</keyword>
-      <keyword>getUint8</keyword>
-      <keyword>setFloat32</keyword>
-      <keyword>setFloat64</keyword>
-      <keyword>setInt16</keyword>
-      <keyword>setInt32</keyword>
-      <keyword>setInt8</keyword>
-      <keyword>setUint16</keyword>
-      <keyword>setUint32</keyword>
-      <keyword>setUint8</keyword>
-    </context>
-
-    <context id="date-methods" style-ref="function">
-      <prefix>\%{is-member}</prefix>
-      <keyword>getDate</keyword>
-      <keyword>getDay</keyword>
-      <keyword>getFullYear</keyword>
-      <keyword>getHours</keyword>
-      <keyword>getMilliseconds</keyword>
-      <keyword>getMinutes</keyword>
-      <keyword>getMonth</keyword>
-      <keyword>getSeconds</keyword>
-      <keyword>getTime</keyword>
-      <keyword>getTimezoneOffset</keyword>
-      <keyword>getUTCDate</keyword>
-      <keyword>getUTCDay</keyword>
-      <keyword>getUTCFullYear</keyword>
-      <keyword>getUTCHours</keyword>
-      <keyword>getUTCMilliseconds</keyword>
-      <keyword>getUTCMinutes</keyword>
-      <keyword>getUTCMonth</keyword>
-      <keyword>getUTCSeconds</keyword>
-      <keyword>getYear</keyword> <!-- deprecated -->
-      <keyword>now</keyword>
-      <keyword>parse</keyword>
-      <keyword>setDate</keyword>
-      <keyword>setFullYear</keyword>
-      <keyword>setHours</keyword>
-      <keyword>setMilliseconds</keyword>
-      <keyword>setMinutes</keyword>
-      <keyword>setMonth</keyword>
-      <keyword>setSeconds</keyword>
-      <keyword>setTime</keyword>
-      <keyword>setUTCDate</keyword>
-      <keyword>setUTCFullYear</keyword>
-      <keyword>setUTCHours</keyword>
-      <keyword>setUTCMilliseconds</keyword>
-      <keyword>setUTCMinutes</keyword>
-      <keyword>setUTCMonth</keyword>
-      <keyword>setUTCSeconds</keyword>
-      <keyword>setYear</keyword> <!-- deprecated -->
-      <keyword>toDateString</keyword>
-      <keyword>toGMTString</keyword> <!-- deprecated -->
-      <keyword>toISOString</keyword>
-      <keyword>toJSON</keyword>
-      <keyword>toLocaleDateString</keyword>
-      <keyword>toLocaleTimeString</keyword>
-      <keyword>toTimeString</keyword>
-      <keyword>toUTCString</keyword>
-      <keyword>UTC</keyword>
-    </context>
-
-    <context id="error-properties" style-ref="properties">
-      <prefix>\%{is-member}</prefix>
-      <keyword>message</keyword>
-      <keyword>name</keyword>
-    </context>
-
-    <context id="function-properties" style-ref="properties">
-      <prefix>\%{is-member}</prefix>
-      <keyword>length</keyword>
-      <keyword>name</keyword>
-      <keyword>target</keyword> <!-- for new.target -->
-    </context>
-
-    <context id="function-methods" style-ref="function">
-      <prefix>\%{is-member}</prefix>
-      <keyword>apply</keyword>
-      <keyword>bind</keyword>
-      <keyword>call</keyword>
-    </context>
-
-    <context id="generator-methods" style-ref="function">
-      <prefix>\%{is-member}</prefix>
-      <keyword>next</keyword>
-      <keyword>return</keyword>
-      <keyword>throw</keyword>
-    </context>
-
-    <context id="json-methods" style-ref="function">
-      <prefix>\%{is-member}</prefix>
-      <keyword>parse</keyword>
-      <keyword>stringify</keyword>
-    </context>
-
-    <context id="map-properties" style-ref="properties">
-      <prefix>\%{is-member}</prefix>
-      <keyword>size</keyword>
-    </context>
-
-    <context id="map-methods" style-ref="function">
-      <prefix>\%{is-member}</prefix>
-      <keyword>clear</keyword>
-      <keyword>delete</keyword>
-      <keyword>entries</keyword>
-      <keyword>forEach</keyword>
-      <keyword>get</keyword>
-      <keyword>has</keyword>
-      <keyword>keys</keyword>
-      <keyword>set</keyword>
-      <keyword>values</keyword>
-    </context>
-
-    <context id="math-properties" style-ref="properties">
-      <prefix>\%{is-member}</prefix>
-      <keyword>E</keyword>
-      <keyword>LN10</keyword>
-      <keyword>LN2</keyword>
-      <keyword>LOG10E</keyword>
-      <keyword>LOG2E</keyword>
-      <keyword>PI</keyword>
-      <keyword>SQRT1_2</keyword>
-      <keyword>SQRT2</keyword>
-    </context>
-
-    <context id="math-methods" style-ref="function">
-      <prefix>\%{is-member}</prefix>
-      <keyword>abs</keyword>
-      <keyword>acosh</keyword>
-      <keyword>acos</keyword>
-      <keyword>asinh</keyword>
-      <keyword>asin</keyword>
-      <keyword>atan2</keyword>
-      <keyword>atanh</keyword>
-      <keyword>atan</keyword>
-      <keyword>cbrt</keyword>
-      <keyword>ceil</keyword>
-      <keyword>clz32</keyword>
-      <keyword>cosh</keyword>
-      <keyword>cos</keyword>
-      <keyword>expm1</keyword>
-      <keyword>exp</keyword>
-      <keyword>floor</keyword>
-      <keyword>fround</keyword>
-      <keyword>hypot</keyword>
-      <keyword>imul</keyword>
-      <keyword>log10</keyword>
-      <keyword>log1p</keyword>
-      <keyword>log2</keyword>
-      <keyword>log</keyword>
-      <keyword>max</keyword>
-      <keyword>min</keyword>
-      <keyword>pow</keyword>
-      <keyword>random</keyword>
-      <keyword>round</keyword>
-      <keyword>sign</keyword>
-      <keyword>sinh</keyword>
-      <keyword>sin</keyword>
-      <keyword>sqrt</keyword>
-      <keyword>tanh</keyword>
-      <keyword>tan</keyword>
-      <keyword>trunc</keyword>
-    </context>
-
-    <context id="number-properties" style-ref="properties">
-      <prefix>\%{is-member}</prefix>
-      <keyword>EPSILON</keyword>
-      <keyword>MAX_SAFE_INTEGER</keyword>
-      <keyword>MAX_VALUE</keyword>
-      <keyword>MIN_SAFE_INTEGER</keyword>
-      <keyword>MIN_VALUE</keyword>
-      <keyword>NaN</keyword>
-      <keyword>NEGATIVE_INFINITY</keyword>
-      <keyword>POSITIVE_INFINITY</keyword>
-    </context>
-
-    <context id="number-methods" style-ref="function">
-      <prefix>\%{is-member}</prefix>
-      <keyword>isFinite</keyword>
-      <keyword>isInteger</keyword>
-      <keyword>isNaN</keyword>
-      <keyword>isSafeInteger</keyword>
-      <keyword>parseFloat</keyword>
-      <keyword>parseInt</keyword>
-      <keyword>toExponential</keyword>
-      <keyword>toFixed</keyword>
-      <keyword>toPrecision</keyword>
-    </context>
-
-    <context id="object-properties" style-ref="properties">
-      <prefix>\%{is-member}</prefix>
-      <keyword>constructor</keyword>
-      <keyword>prototype</keyword>
-    </context>
-
-    <context id="object-methods" style-ref="function">
-      <prefix>\%{is-member}</prefix>
-      <keyword>assign</keyword>
-      <keyword>create</keyword>
-      <keyword>defineProperties</keyword>
-      <keyword>defineProperty</keyword>
-      <keyword>freeze</keyword>
-      <keyword>getOwnPropertyDescriptor</keyword>
-      <keyword>getOwnPropertyNames</keyword>
-      <keyword>getOwnPropertySymbols</keyword>
-      <keyword>getPrototypeOf</keyword>
-      <keyword>hasOwnProperty</keyword>
-      <keyword>isExtensible</keyword>
-      <keyword>isFrozen</keyword>
-      <keyword>isPrototypeOf</keyword>
-      <keyword>isSealed</keyword>
-      <keyword>is</keyword>
-      <keyword>keys</keyword>
-      <keyword>preventExtensions</keyword>
-      <keyword>propertyIsEnumerable</keyword>
-      <keyword>seal</keyword>
-      <keyword>setPrototypeOf</keyword>
-      <keyword>toLocaleString</keyword>
-      <keyword>toString</keyword>
-      <keyword>valueOf</keyword>
-    </context>
-
-    <context id="promise-methods" style-ref="function">
-      <prefix>\%{is-member}</prefix>
-      <keyword>all</keyword>
-      <keyword>catch</keyword>
-      <keyword>race</keyword>
-      <keyword>reject</keyword>
-      <keyword>resolve</keyword>
-      <keyword>then</keyword>
-    </context>
-
-    <context id="proxy-methods" style-ref="function">
-      <prefix>\%{is-member}</prefix>
-      <keyword>revocable</keyword>
-    </context>
-
-    <context id="reflect-methods" style-ref="function">
-      <prefix>\%{is-member}</prefix>
-      <keyword>apply</keyword>
-      <keyword>construct</keyword>
-      <keyword>defineProperty</keyword>
-      <keyword>deleteProperty</keyword>
-      <keyword>getOwnPropertyDescriptor</keyword>
-      <keyword>getPrototypeOf</keyword>
-      <keyword>get</keyword>
-      <keyword>has</keyword>
-      <keyword>isExtensible</keyword>
-      <keyword>ownKeys</keyword>
-      <keyword>preventExtensions</keyword>
-      <keyword>setPrototypeOf</keyword>
-      <keyword>set</keyword>
-    </context>
-
-    <context id="regexp-properties" style-ref="properties">
-      <prefix>\%{is-member}</prefix>
-      <keyword>flags</keyword>
-      <keyword>global</keyword>
-      <keyword>ignoreCase</keyword>
-      <keyword>lastIndex</keyword>
-      <keyword>multiline</keyword>
-      <keyword>source</keyword>
-      <keyword>sticky</keyword>
-      <keyword>unicode</keyword>
-    </context>
-
-    <context id="regexp-methods" style-ref="function">
-      <prefix>\%{is-member}</prefix>
-      <keyword>exec</keyword>
-      <keyword>test</keyword>
-    </context>
-
-    <context id="set-properties" style-ref="properties">
-      <prefix>\%{is-member}</prefix>
-      <keyword>size</keyword>
-    </context>
-
-    <context id="set-methods" style-ref="function">
-      <prefix>\%{is-member}</prefix>
-      <keyword>add</keyword>
-      <keyword>clear</keyword>
-      <keyword>delete</keyword>
-      <keyword>entries</keyword>
-      <keyword>forEach</keyword>
-      <keyword>has</keyword>
-      <keyword>keys</keyword>
-      <keyword>values</keyword>
-    </context>
-
-    <context id="string-properties" style-ref="properties">
-      <prefix>\%{is-member}</prefix>
-      <keyword>length</keyword>
-    </context>
-
-    <context id="string-methods" style-ref="function">
-      <prefix>\%{is-member}</prefix>
-      <keyword>charAt</keyword>
-      <keyword>charCodeAt</keyword>
-      <keyword>codePointAt</keyword>
-      <keyword>concat</keyword>
-      <keyword>endsWith</keyword>
-      <keyword>fromCharCode</keyword>
-      <keyword>fromCodePoint</keyword>
-      <keyword>includes</keyword>
-      <keyword>indexOf</keyword>
-      <keyword>lastIndexOf</keyword>
-      <keyword>localeCompare</keyword>
-      <keyword>match</keyword>
-      <keyword>normalize</keyword>
-      <keyword>raw</keyword>
-      <keyword>repeat</keyword>
-      <keyword>replace</keyword>
-      <keyword>search</keyword>
-      <keyword>slice</keyword>
-      <keyword>split</keyword>
-      <keyword>startsWith</keyword>
-      <keyword>substring</keyword>
-      <keyword>substr</keyword> <!-- deprecated -->
-      <keyword>toLocaleLowerCase</keyword>
-      <keyword>toLocaleUpperCase</keyword>
-      <keyword>toLowerCase</keyword>
-      <keyword>toUpperCase</keyword>
-      <keyword>trim</keyword>
-    </context>
-
-    <context id="symbol-properties" style-ref="properties">
-      <prefix>\%{is-member}</prefix>
-      <keyword>hasInstance</keyword>
-      <keyword>isConcatSpreadable</keyword>
-      <keyword>iterator</keyword>
-      <keyword>match</keyword>
-      <keyword>replace</keyword>
-      <keyword>search</keyword>
-      <keyword>species</keyword>
-      <keyword>split</keyword>
-      <keyword>toPrimitive</keyword>
-      <keyword>toStringTag</keyword>
-      <keyword>unscopables</keyword>
-    </context>
-
-    <context id="symbol-methods" style-ref="function">
-      <prefix>\%{is-member}</prefix>
-      <keyword>for</keyword>
-      <keyword>keyFor</keyword>
-    </context>
-
-    <!-- TypedArray is one of these objects:
-         * Float32Array
-         * Float64Array
-         * Int16Array
-         * Int32Array
-         * Int8Array
-         * Uint16Array
-         * Uint32Array
-         * Uint8Array
-         * Uint8ClampedArray
+    <!-- <ObjectBindingPattern> -->
+    <context id="_choice-object-destructuring" style-ref="object-destructuring" end-parent="true">
+      <start>{</start>
+      <end>}</end>
+      <include>
+        <context ref="embedded-lang-hooks"/>
+        <context ref="comments"/>
+
+        <context id="_object-destructuring-content">
+          <include>
+
+            <context id="_object-destructuring-first-property" once-only="true">
+              <start>\%{before-next-token}</start>
+              <end>\%{before-next-token}</end>
+              <include>
+                <context ref="embedded-lang-hooks"/>
+                <context ref="comments"/>
+                <context ref="_object-destructuring-property-content"/>
+              </include>
+            </context> <!-- /_object-destructuring-properties -->
+
+            <context id="_object-destructuring-properties">
+              <start>,</start>
+              <end>\%{before-next-token}</end>
+              <include>
+                <context ref="embedded-lang-hooks"/>
+                <context ref="comments"/>
+                <context ref="_object-destructuring-property-content"/>
+              </include>
+            </context> <!-- /_object-destructuring-properties -->
+
+          </include>
+        </context> <!-- /_object-destructuring-content -->
+
+      </include>
+    </context> <!-- /_choice-object-destructuring -->
+
+    <!-- ## Assignment target
+
+         Things that can appear on the left side of an equals sign
+         (identifier or array/object destructuring assignment), in all
+         places (function parameters, variable declaration) *except* in
+         assignment expressions (array/object literals will match for
+         array/object destructuring)
     -->
-    <context id="typedarray-properties" style-ref="properties">
-      <prefix>\%{is-member}</prefix>
-      <keyword>buffer</keyword>
-      <keyword>byteLength</keyword>
-      <keyword>byteOffset</keyword>
-      <keyword>BYTES_PER_ELEMENT</keyword>
-      <keyword>length</keyword>
-      <keyword>name</keyword>
-    </context>
-
-    <context id="typedarray-methods" style-ref="function">
-      <prefix>\%{is-member}</prefix>
-      <keyword>copyWithin</keyword>
-      <keyword>entries</keyword>
-      <keyword>every</keyword>
-      <keyword>fill</keyword>
-      <keyword>filter</keyword>
-      <keyword>findIndex</keyword>
-      <keyword>find</keyword>
-      <keyword>forEach</keyword>
-      <keyword>from</keyword>
-      <keyword>indexOf</keyword>
-      <keyword>join</keyword>
-      <keyword>keys</keyword>
-      <keyword>lastIndexOf</keyword>
-      <keyword>map</keyword>
-      <keyword>of</keyword>
-      <keyword>reduceRight</keyword>
-      <keyword>reduce</keyword>
-      <keyword>reverse</keyword>
-      <keyword>set</keyword>
-      <keyword>slice</keyword>
-      <keyword>some</keyword>
-      <keyword>sort</keyword>
-      <keyword>subarray</keyword>
-      <keyword>values</keyword>
-    </context>
-
-    <context id="weakmap-methods" style-ref="function">
-      <prefix>\%{is-member}</prefix>
-      <keyword>delete</keyword>
-      <keyword>get</keyword>
-      <keyword>has</keyword>
-      <keyword>set</keyword>
-    </context>
-
-    <context id="weakset-methods" style-ref="function">
-      <prefix>\%{is-member}</prefix>
-      <keyword>add</keyword>
-      <keyword>delete</keyword>
-      <keyword>has</keyword>
-    </context>
-
-    <context id="bigint-methods" style-ref="function">
-      <prefix>\%{is-member}</prefix>
-      <keyword>asIntN</keyword>
-      <keyword>asUintN</keyword>
-    </context>
-
-    <context id="constructors" style-ref="constructors">
-      <keyword>ArrayBuffer</keyword>
-      <keyword>Array</keyword>
-      <keyword>BigInt</keyword>
-      <keyword>Boolean</keyword>
-      <keyword>Date</keyword>
-      <keyword>Error</keyword>
-      <keyword>EvalError</keyword>
-      <keyword>Float32Array</keyword>
-      <keyword>Float64Array</keyword>
-      <keyword>Function</keyword>
-      <keyword>Int16Array</keyword>
-      <keyword>Int32Array</keyword>
-      <keyword>Int8Array</keyword>
-      <keyword>JSON</keyword>
-      <keyword>Map</keyword>
-      <keyword>Math</keyword>
-      <keyword>Number</keyword>
-      <keyword>Object</keyword>
-      <keyword>Promise</keyword>
-      <keyword>Proxy</keyword>
-      <keyword>RangeError</keyword>
-      <keyword>ReferenceError</keyword>
-      <keyword>Reflect</keyword>
-      <keyword>RegExp</keyword>
-      <keyword>Set</keyword>
-      <keyword>String</keyword>
-      <keyword>Symbol</keyword>
-      <keyword>SyntaxError</keyword>
-      <keyword>TypeError</keyword>
-      <keyword>Uint16Array</keyword>
-      <keyword>Uint32Array</keyword>
-      <keyword>Uint8Array</keyword>
-      <keyword>Uint8ClampedArray</keyword>
-      <keyword>URIError</keyword>
-      <keyword>WeakMap</keyword>
-      <keyword>WeakSet</keyword>
-    </context>
-
-    <context id="future-words" style-ref="future-words">
-      <keyword>enum</keyword>
-
-      <!-- in strict mode -->
-      <keyword>implements</keyword>
-      <keyword>interface</keyword>
-      <keyword>package</keyword>
-      <keyword>private</keyword>
-      <keyword>protected</keyword>
-      <keyword>public</keyword>
-    </context>
-
-    <context id="template-string" style-ref="string" class="string" class-disabled="no-spell-check">
-      <start>`</start>
-      <end>`</end>
+
+    <!-- <VariableDeclaration> / <LexicalBinding> -->
+    <context id="_assignment-target" once-only="true">
+      <start>\%{before-next-token}</start>
+      <end>\%{before-next-token}</end>
       <include>
-        <context ref="escape"/>
-        <context ref="def:line-continue"/>
+        <context ref="embedded-lang-hooks"/>
+        <context ref="comments"/>
 
-        <!-- FIXME: syntax highlight should go back to none here -->
-        <context id="template-expression" style-ref="function">
-          <start>\$\{</start>
-          <end>\}</end>
+        <context id="_assignment-target-content">
           <include>
-            <context ref="js:*"/>
+            <context ref="_choice-array-destructuring"/>
+            <context ref="_choice-object-destructuring"/>
+            <context ref="choice-identifier"/>
           </include>
-        </context>
+        </context> <!-- /_assignment-target-content -->
+
       </include>
-    </context>
+    </context> <!-- /_assignment-target -->
+
+    <context id="ordered-assignment-target" once-only="true">
+      <start>\%{before-next-token}</start>
+      <end>\%{before-next-token}</end>
+      <include>
+        <context ref="_assignment-target"/>
+      </include>
+    </context> <!-- /ordered-assignment-target -->
+
+
+    <!-- Approximate code order for component files:
+         * javascript-literals.lang
+         * javascript-values.lang
+         * javascript-functions-classes.lang
+         * javascript-expressions.lang
+         * javascript-statements.lang
+         * javascript-modules.lang
+    -->
+
+
+    <!-- # Main context -->
 
-    <!--main context-->
     <context id="js" class="no-spell-check">
       <include>
-        <context ref="def:c-like-comment"/>
-        <context ref="def:c-like-comment-multiline"/>
-        <context ref="def:c-like-close-comment-outside-comment"/>
-        <context ref="string" />
-        <context ref="single-quoted-string" />
-        <context ref="template-string"/>
-        <context ref="decimal"/>
-        <context ref="binary-integer"/>
-        <context ref="octal-integer"/>
-        <context ref="hex-integer"/>
-        <context ref="big-integer"/>
-        <context ref="legacy-octal-integer"/>
-        <context ref="undefined-value"/>
-        <context ref="null-value"/>
-        <context ref="boolean"/>
-        <context ref="types"/>
-        <context ref="global-functions"/>
-        <context ref="array-properties"/>
-        <context ref="array-methods"/>
-        <context ref="arraybuffer-properties"/>
-        <context ref="arraybuffer-methods"/>
-        <context ref="dataview-properties"/>
-        <context ref="dataview-methods"/>
-        <context ref="date-methods"/>
-        <context ref="error-properties"/>
-        <context ref="function-properties"/>
-        <context ref="function-methods"/>
-        <context ref="generator-methods"/>
-        <context ref="json-methods"/>
-        <context ref="map-properties"/>
-        <context ref="map-methods"/>
-        <context ref="math-properties"/>
-        <context ref="math-methods"/>
-        <context ref="number-properties"/>
-        <context ref="number-methods"/>
-        <context ref="object-properties"/>
-        <context ref="object-methods"/>
-        <context ref="promise-methods"/>
-        <context ref="proxy-methods"/>
-        <context ref="reflect-methods"/>
-        <context ref="regexp-properties"/>
-        <context ref="regexp-methods"/>
-        <context ref="set-properties"/>
-        <context ref="set-methods"/>
-        <context ref="string-properties"/>
-        <context ref="string-methods"/>
-        <context ref="symbol-properties"/>
-        <context ref="symbol-methods"/>
-        <context ref="typedarray-properties"/>
-        <context ref="typedarray-methods"/>
-        <context ref="weakmap-methods"/>
-        <context ref="weakset-methods"/>
-        <context ref="bigint-methods"/>
-        <context ref="constructors"/>
-        <context ref="keywords"/>
-        <context ref="future-words"/>
-        <context ref="regex-simple"/>
+        <context ref="embedded-lang-hooks"/>
+        <context ref="comments"/>
+        <context ref="js-st:directives"/>
+        <context ref="js-mod:export-declarations"/>
+        <context ref="js-mod:import-declarations"/>
+        <context ref="js-st:statements"/>
       </include>
-    </context>
-    <!--main context-->
+    </context> <!-- /js -->
 
   </definitions>
 </language>
diff --git a/data/language-specs/json.lang b/data/language-specs/json.lang
index aebb8f5c..6f9bc817 100644
--- a/data/language-specs/json.lang
+++ b/data/language-specs/json.lang
@@ -110,8 +110,8 @@
       <context ref="string"/>
       <context ref="decimal"/>
       <context ref="float"/>
-      <context ref="js:null-value" style-ref="null-value"/>
-      <context ref="js:boolean" style-ref="boolean"/>
+      <context ref="js-lit:null-value" style-ref="null-value"/>
+      <context ref="js-lit:boolean" style-ref="boolean"/>
       <context ref="catchall"/>
     </include></context>
 
diff --git a/data/styles/classic.xml b/data/styles/classic.xml
index bc35f93e..d61fa964 100644
--- a/data/styles/classic.xml
+++ b/data/styles/classic.xml
@@ -106,8 +106,7 @@
   <style name="xml:attribute-name"          foreground="violet"/>
   <style name="xml:namespace"               foreground="green" bold="true"/>
 
-  <style name="js:object"                   foreground="#2E8B57" bold="true"/>
-  <style name="js:constructors"             foreground="#008B8B"/>
+  <style name="js:built-in-constructor"     use-style="def:identifier"/>
 
   <style name="latex:display-math"          foreground="#6A5ACD"/>
   <style name="latex:command"               foreground="#2E8B57" bold="true"/>
diff --git a/data/styles/kate.xml b/data/styles/kate.xml
index 369632a5..c0a91455 100644
--- a/data/styles/kate.xml
+++ b/data/styles/kate.xml
@@ -128,8 +128,7 @@
   <style name="docbook:gui-elements"        use-style="docbook-element"/>
   <style name="docbook:structural-elements" use-style="docbook-element"/>
 
-  <style name="js:object"                   foreground="dark-green"/>
-  <style name="js:constructors"             bold="true"/>
+  <style name="js:built-in-constructor"     bold="true"/>
 
   <style name="mooscript:special-vars"      use-style="c:preprocessor"/>
 
diff --git a/data/styles/tango.xml b/data/styles/tango.xml
index f4a38866..3f9ff0d1 100644
--- a/data/styles/tango.xml
+++ b/data/styles/tango.xml
@@ -117,8 +117,7 @@
   <style name="xml:tags"                    foreground="chameleon3"/>
   <style name="xml:namespace"               bold="true"/>
 
-  <style name="js:object"                   foreground="chameleon3" bold="true"/>
-  <style name="js:constructors"             foreground="chameleon3"/>
+  <style name="js:built-in-constructor"     foreground="chameleon3"/>
 
   <style name="latex:display-math"          foreground="plum3"/>
   <style name="latex:command"               foreground="chameleon3" bold="true"/>
diff --git a/po/POTFILES.skip b/po/POTFILES.skip
index 9e0b7358..578f211e 100644
--- a/po/POTFILES.skip
+++ b/po/POTFILES.skip
@@ -68,7 +68,13 @@ data/language-specs/imagej.lang
 data/language-specs/ini.lang
 data/language-specs/jade.lang
 data/language-specs/java.lang
+data/language-specs/javascript-expressions.lang
+data/language-specs/javascript-functions-classes.lang
 data/language-specs/javascript.lang
+data/language-specs/javascript-literals.lang
+data/language-specs/javascript-modules.lang
+data/language-specs/javascript-statements.lang
+data/language-specs/javascript-values.lang
 data/language-specs/j.lang
 data/language-specs/json.lang
 data/language-specs/julia.lang
diff --git a/tests/syntax-highlighting/file.html b/tests/syntax-highlighting/file.html
index 699360d1..9cfcedd5 100644
--- a/tests/syntax-highlighting/file.html
+++ b/tests/syntax-highlighting/file.html
@@ -9,6 +9,12 @@
       pre.display {font-family: serif}
       pre.format {font-family: serif}
     </style>
+    <script type="text/javascript">
+      window.addEventListener('click', function (e) {
+        e.preventDefault();
+        alert('Hi there!');
+      }, false);
+    </script>
   </head>
   <body lang="en" bgcolor="#FFFFFF" text="#000000" link="#0000FF"
         vlink="#800080" alink="#FF0000">
diff --git a/tests/syntax-highlighting/file.js b/tests/syntax-highlighting/file.js
index 42ba602d..2e023514 100644
--- a/tests/syntax-highlighting/file.js
+++ b/tests/syntax-highlighting/file.js
@@ -1,64 +1,816 @@
-// Regular expressions:
-/abc/
-x = /abc/;
+/*
+ * Expressions (in expression statements)
+ */
+
+/*
+ * Literals
+ */
+
+/* Keyword values */
+
+var NULL = null;
+var TRUE = true;
+var FALSE = false;
+
+
+/* Number */
+
+var decimal1 = 0;
+var decimal2 = 123.45;
+var decimal3 = .66667;
+var decimal4 = 10e20;
+var decimal5 = 0.2e+1;
+var decimal6 = .5E-20;
+var hex1 = 0xDEADBEEF;
+var hex2 = 0Xcafebabe;
+
+// ES2015 binary and octal numbers
+let binary1 = 0b1010;
+let binary2 = 0B00001111;
+let octal1 = 0o0123;
+let octal2 = 0O4567;
+
+// Legacy octal numbers
+var legacy_octal1 = 01;
+var legacy_octal2 = 007;
+
+// BigInt (ES2020)
+var decimal1 = 0n;
+var decimal2 = 123n;
+var hex1 = 0xDEADBEEFn;
+var hex2 = 0Xcafebaben;
+var binary1 = 0b1010n;
+var binary2 = 0B00001111n;
+var octal1 = 0o0123n;
+var octal2 = 0O4567n;
+
+
+/* String */
+
+// Escape sequences
+'\b\f\n\r\t\v\0\'\"\\'; // Single character escape
+"\1\01\001";            // Octal escape (Annex B)
+'\xA9';                 // Hexadecimal escape
+"\u00a9";               // Unicode escape
+'\u{1D306}';            // Unicode code point escape
+
+
+/* Array literal */
+
+[];
+[1];
+[1.0, 'two', 0x03];
+
+// Trailing comma
+[
+    [1,2,3],
+    [4,5,6],
+];
+
+// Spread syntax
+[1, ...a, 2];
+
+
+/* Object literal */
+
+a = {};
+a = { prop: 'value' };
+a = { prop: 'value', extends: 1 };
+
+// Trailing comma
+a = {
+    prop: 'value',
+    extends: 1,
+};
+
+// Shorthand property names
+a = { b, c, d };
+
+// Getter / setter
+a = {
+    _hidden: null,
+    get property() { return _hidden; },
+    set property(value) { this._hidden = value; }
+};
+
+// Shorthand function notation
+a = {
+    method() {},
+    *generator() {},
+
+    // Async function (ES2017)
+    async method() {},
+    async /* comment */ method() {},
+    async() {},// method called "async"
+    async: false, // property called "async"
+    async prop: 'val', // incorrectly highlighted (syntax error)
+
+    // Async generator (ES2018)
+    async *generator() {}
+};
+
+// Computed property names
+a = {
+    ['prop']: 1,
+    ['method']() {}
+};
+
+// Spread properties (ES2018)
+a = { ...b };
+
+
+/* Regular expression literal */
+
+/abc/;
+x = /abc/gi;
 function_with_regex_arg(/abc/);
-[ /abc/, /def/];
-{ regex: /abc/ };
+[ /abc/m, /def/u ];
+a = { regex: /abc/s }; // s (dotAll): ES2018
 (1 === 0) ? /abc/ : /def/;
-/abc/ /* Comment */
-/abc/ // Comment
+/abc/; /* Comment */
+/abc/; // Comment
 var matches = /abc/.exec('Alphabet ... that should contain abc, right?');
 
-// No regex here: 
+// No regex here
 a = [thing / thing, thing / thing];
 x = a /b/ c / d;
 
-// Character groups with backslashes:
-/[ab\\]/ // a, b or backslash
-/[ab\]]/ // a, b or ]
-/\\[ab]/ // a or b preceded by backslash
-/\[ab]/  // Literally "[ab]"
-
-// Escape sequences:
-'\b\f\n\r\t\v\0\'\"\\' // Single character escape
-"\1\01\001"            // Octal escape
-'\xA9'                 // Hexadecimal escape
-"\u00a9"               // Unicode escape
-'\u{1D306}'            // Unicode code point escape
-/\cJ/                  // Control escape
-'\
-'                      // Newline escape
-
-// ES2015 binary and octal numbers:
-let binary1 = 0b1010;
-let binary2 = 0B00001111;
-let octal1 = 0o0123;
-let octal2 = 0O4567;
+// Character groups with backslashes
+/[ab\\]/; // a, b or backslash
+/[ab\]]/; // a, b or ]
+/\\[ab]/; // a or b preceded by backslash
+/\[ab]/;  // Literally "[ab]"
 
-// Big integers:
-let bigInt = 543789527895762347856234897532n;
-let bigIntZero = 0n;
-let bigIntConst = BigInt('1234513123123');
-312312n+5234523n-12n*3123n/4n%3231n**-123123n;
+// Control escape
+/\cJ/;
 
-// Template strings
-// ----------------
-// Template strings are delimited by back-ticks (grave accent) and
-// can span multiple lines. They allow for expressions that inside
-// of a dollar-sign plus curly-bracket construct (${...}).
+// Unicode property escape (ES2018)
+/\p{General_Category=Letter}/u;
+/\p{Letter}/u;
 
-console.log(`The sum of 2 and 2 is ${2 + 2}`);
+// Named capture groups (ES2018)
+/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
+/(?<foobar>foo|bar)/u;
+/^(?<half>.*).\k<half>$/u; // backreference
 
-// Interpolation inside interpolation
-let interp = `Hello ${
-  Math.random() > 0.5
-  ? 'World!'
-  : `${Math.random() > 0.5 ? 'Alice' : 'Bob'}!`
-}`;
+/* Template literal */
+
+console.log(`The sum of 2 and 2 is ${2 + 2}`);
 
 let y = 8;
 let my_string = `This is a multiline
 string that also contains
 a template ${y + (4.1 - 2.2)}`;
-let escaped = `\b\f\`\\\u00a8\u{12345}\
-`;
+
+
+/*
+ * Built-in values
+ */
+
+// global object values
+Infinity;
+NaN;
+undefined;
+
+// global object functions
+decodeURIComponent();
+decodeURI();
+encodeURIComponent();
+encodeURI();
+eval();
+isFinite();
+isNaN();
+parseFloat();
+parseInt();
+
+// constructors (subset)
+Array();
+BigInt(); // ES2020
+Boolean();
+Date();
+Error();
+Function();
+Map();
+Object();
+Promise();
+RegExp();
+Set();
+String();
+Symbol();
+
+// objects
+JSON.parse();
+Math.random();
+
+// object keywords
+arguments;
+globalThis; // ES2020
+new.target;
+new . /* comment */ target;
+super;
+this;
+new . /* comment
+*/ target; // not correctly highlighted
+new // comment
+.target; // not correctly highlighted
+
+// function keywords
+import(); // ES2020
+import /* comment */ (); // ES2020
+import /* comment
+*/ (); // not correctly highlighted (though it may appear correct)
+import // comment
+(); // not correctly highlighted (though it may appear correct)
+
+// properties (subset)
+array.length;
+Math.PI;
+Number.NaN;
+object.constructor;
+Class.prototype;
+Symbol.asyncIterator; // ES2018
+Symbol('desc').description; // ES2019
+
+// methods (subset)
+array.keys();
+date.toString();
+object.valueOf();
+re.test();
+array.includes(); // ES2016
+Object.values(); // ES2017
+Object.entries(); // ES2017
+string.padStart(); // ES2017
+string.padEnd(); // ES2017
+Object.getOwnPropertyDescriptors(); // ES2017
+promise.finally(); // ES2018
+Object.fromEntries(); // ES2019
+string.trimStart(); // ES2019
+string.trimEnd(); // ES2019
+array.flat(); // ES2019
+array.flatMap(); // ES2019
+string.matchAll(); // ES2020
+Promise.allSettled(); // ES2020
+BigInt.asUintN(); // ES2020
+
+
+/*
+ * Function expression, arrow function
+ */
+
+a = function () { return 1 };
+a = function fn() {
+    return;
+};
+a = function fn(x) {};
+a = function fn(x, y) {};
+
+// Arrow function
+x => -x;
+() => {};
+(x, y) => x + y;
+(x, y) => { return x + y; };
+(x, y) => /* comment */ { return x + y; } /* comment */ ;
+(x) => ({ a: x }); // return object
+
+// Default parameters
+a = function fn(x, y = 1) {};
+(x, y = 1) => x + y;
+
+// Parameter without default after default parameters
+a = function fn(x = 1, y) {};
+(x = 1, y) => x + y;
+
+// Array destructuring
+a = function fn([x]) {};
+a = function fn([x, y]) {};
+([x]) => x;
+([x, y]) => x + y;
+
+// Object destructuring
+a = function fn({ x }) {};
+a = function fn({ x, b: y }) {};
+({ x }) => x;
+({ x, b: y }) => x + y;
+
+// Destructuring and default value
+a = function f([x, y] = [1, 2], {c: z} = {c: 3}) {};
+([x, y] = [1, 2], {c: z} = {c: x + y}) => x + y + z;
+
+// Generator function
+a = function*fn() {};
+a = function * fn() {};
+
+// Rest parameters
+a = function fn(...rest) {};
+a = function fn(x, y, ...rest) {};
+(...rest) => rest;
+(x, y, ...rest) => rest;
+
+// Async function (ES2017)
+a = async function fn() {};
+a = async /* comment */ function fn() {};
+a = async /* comment
+*/ function fn() {}; // correctly highlighted, because async cannot be followed by a line terminator
+async x => x;
+async () => {};
+async /* comment */ () => {};
+async /* comment
+*/ () => {}; // correctly highlighted, because async cannot be followed by a line terminator
+async(); // incorrectly highlighted
+
+// Async generator (ES2018)
+a = async function * fn() {};
+
+// Trailing comma (ES2017)
+a = function fn(x, y,) {};
+(x, y,) => x + y;
+
+// Trailing comma after rest parameters (syntax error)
+a = function fn(x, y, ...rest,) {};
+(x, y, ...rest,) => rest;
+
+
+/*
+ * Class expression
+ */
+
+a = class Foo {
+    constructor() {
+    }
+    method(x, y) {
+        return x + y;
+    }
+    *generator() {}
+};
+a = class extends Bar {
+    constructor() {
+        this._value = null;
+    }
+    get property() {
+        return this._value;
+    }
+    set property(x) {
+        this._value = x;
+    }
+    static get bar() {
+        return 'bar';
+    }
+};
+
+
+/*
+ * Operators
+ * use groupings to test, as there can only be one expression (in the
+ * first grouping item)
+ */
+
+// Grouping
+( 1 + 2 );
+
+// Increment / decrement
+( ++a );
+( --a );
+( a++ );
+( a-- );
+
+// Keyword unary
+( await promise() ); // ES2017
+( delete obj.prop );
+( new Array() );
+( void 1 );
+( typeof 'str' );
+( yield 1 );
+( yield* fn() );
+
+// Arithmetic
+( 1 + 2 );
+( 1 - 2 );
+( 1 * 2 );
+( 1 / 2 );
+( 1 % 2 );
+( 1 ** 2 ); // ES2016
+( +1 );
+( -1 );
+
+// Keyword relational
+( prop in obj );
+( obj instanceof constructor );
+
+// Comparison
+( 1 == 2 );
+( 1 != 2 );
+( 1 === 2 );
+( 1 !== 2 );
+( 1 < 2 );
+( 1 > 2 );
+( 1 <= 2 );
+( 1 >= 2 );
+
+// Bitwise
+( 1 & 2 );
+( 1 | 2 );
+( 1 ^ 2 );
+( ~1 );
+( 1 << 2 );
+( 1 >> 2 );
+( 1 >>> 2 );
+
+// Logical
+( 1 && 2 );
+( 1 || 2 );
+( !1 );
+
+// Assignment
+( a = 1 );
+( a += 1 );
+( a -= 1 );
+( a *= 1 );
+( a /= 1 );
+( a %= 1 );
+( a **= 1 ); // ES2016
+( a <<= 1 );
+( a >>= 1 );
+( a >>>= 1 );
+( a &= 1 );
+( a |= 1 );
+( a ^= 1 );
+( [a, b] = [1, 2] ); // array destructuring
+( {a, b} = { a: 1, b: 2} ); // object destructuring
+
+// Comma
+1, 2 ;
+
+// Conditional / ternary
+( true ? 1 : 2 );
+( true ? : 2 ); // missing true value (syntax error)
+
+
+/*
+ * Property accessors
+ */
+
+// Dot notation
+arr.length;
+obj
+    . prototype
+    . constructor;
+
+// Bracket notation
+arr['length'];
+obj
+    ['prototype']
+    ['constructor'];
+
+// Mixed
+obj
+    ['prototype']
+    . constructor;
+obj
+    . prototype
+    ['constructor'];
+
+
+/*
+ * Function call
+ */
+
+fn();
+obj.fn(1);
+obj['fn'](1, 2);
+
+// Spread syntax
+fn(x, y, ...args);
+
+// Trailing comma (ES2017)
+fn(x, y,);
+
+
+/* Tagged template */
+
+myTag`That ${ person } is ${ age }`;
+
+
+/*
+ * Statements and declarations
+ */
+
+/* Use strict directive */
+
+"use strict";
+function () {
+    'use strict';
+}
+
+// invalid directives
+" use strict";
+'use strict ';
+"use  strict";
+'use   strict';
+"hello 'use strict' world";
+fn("use strict");
+{ 'use strict'; }
+
+
+/* Block statement */
+
+{
+    hello();
+    world();
+}
+{ hello(); world() }
+
+
+/* Break statement */
+
+break;
+break label;
+break       // end statement
+    label;  // separate statement
+{ break }
+
+
+/*
+ * Class declaration
+ */
+
+class Foo {
+    constructor() {
+    }
+    method(x, y) {
+        return x + y;
+    }
+    *generator() {}
+}
+class Foo extends Bar {
+    constructor() {
+        this._value = null;
+    }
+    get property() {
+        return this._value;
+    }
+    set property(x) {
+        this._value = x;
+    }
+    static get bar() {
+        return 'bar';
+    }
+}
+
+
+/* Continue statement */
+
+continue;
+continue label;
+continue    // end statement
+    label;  // separate statement
+{ continue }
+
+
+/* Debugger statement */
+
+debugger;
+debugger
+    ;
+
+
+/* Export / import statement */
+
+export { a };
+export { a, b, };
+export { x as a };
+export { x as a, y as b, };
+export var a;
+export let a, b;
+export const a = 1;
+export var a = 1, b = 2;
+export function fn() {}
+export function* fn() {}
+export class Class {}
+
+export default 1;
+export default function () {}
+export default function *fn() {}
+export default class {}
+export { a as default, b };
+
+export * from 'module';
+export { a, b } from 'module';
+export { x as a, y as b, } from 'module';
+export { default } from 'module';
+
+import a from "module";
+import * as ns from "module";
+import { a } from "module";
+import { a, b } from "module";
+import { x as a } from "module";
+import { x as a, y as b } from "module";
+import { default as a } from "module";
+import a, { b } from "module";
+import a, * as nm from "module";
+import "module";
+
+
+/* For statement */
+
+for (i = 0; i < 10; i++) something();
+for (var i = 10; i >= 0; i--) {
+    something();
+}
+for (i = 0, j = 0; i < 10; i++, j++) something();
+for (let i = 10, j = 0; i >= 0; i--, j += 1) {
+    something();
+}
+for (prop in obj) {} // matches "in" binary operator instead
+for (const prop in obj) {}
+for (val of generator()) {}
+for (var val of array) {}
+for await (let x of asyncIterable) {} // ES2018
+for /* comment */ await /* comment */ (let x of asyncIterable) {} // ES2018
+
+
+/* Function declaration statement */
+
+function fn() {
+    return;
+}
+async function fn() {} // ES2017
+async /* comment */ function fn() {} // ES2017
+async /* comment
+*/ function fn() {} // correctly highlighted, because async cannot be followed by a line terminator
+
+
+/* If..else statement */
+
+if (a < 0) lessThan(); else if (a > 0) greaterThan(); else equal();
+if (a < 0)
+    lessThan();
+else if (a > 0)
+    greaterThan();
+else
+    equal();
+if (a < 0) {
+    lessThan();
+} else if (a > 0) {
+    greaterThan();
+} else {
+    equal();
+}
+
+
+/* Label statement */
+
+outer: for (var i = 0; i < 10; i++) {
+inner /* comment */ : for (var j = 0; j < 2; j++) {}
+}
+loop /* comment
+*/ : for (var i in obj) {} // incorrectly highlighted (though it may appear correct)
+
+
+/* Return statement */
+
+return;
+return 1;
+return  // end statement
+    1;  // separate statement
+return (
+    1
+);
+{ return a }
+
+
+/* Switch statement */
+
+switch (foo) {
+case 1:
+    doIt();
+    break;
+case '2':
+    doSomethingElse();
+    break;
+default:
+    oops();
+}
+
+
+/* Throw statement */
+
+throw e;
+throw new Error();
+throw   // end statement (syntax error)
+    e;  // separate statement
+throw (
+    new Error()
+);
+{ throw new Error() }
+
+
+/* Try...catch statement */
+
+try {
+    somethingDangerous();
+}
+catch (e) {
+    didntWork(e);
+}
+catch { // ES2019
+    return false;
+}
+finally {
+    cleanup();
+}
+
+
+/* Variable declaration */
+
+// Declaration only
+const a;
+let a, b, c;
+var a
+    ,
+    b
+    ,
+    c
+    ;
+
+// With assignment
+const a = 1;
+let a = 1, b = [2, 3], c = 4;
+var a
+    =
+    1
+    ,
+    b
+    =
+    [
+    2
+    ,
+    3
+    ]
+    ,
+    c
+    =
+    4
+    ;
+
+// Array destructuring
+var [a, b] = [1, 2];
+var [
+    a
+    ,
+    b
+    ]
+    =
+    [
+    1
+    ,
+    2
+    ]
+    ;
+var [a = 5, b = 7] = [1]; // default values
+var [a, , b] = f(); // ignoring some returned values
+var [a, ...b] = [1, 2, 3]; // rest syntax
+
+// Object destructuring
+var { a, b } = { a: 1, b: 2 };
+var {
+    a
+    ,
+    b
+    }
+    =
+    {
+    a
+    :
+    1
+    ,
+    b
+    :
+    2
+    }
+    ;
+var { a: foo, b: bar } = { a: 1, b: 2 }; // assigning to new variable names
+var { a = 5, b = 7 } = { a: 1 }; // default values
+var { a: foo = 5, b: bar = 7 } = { a: 1 }; // assigning to new variable names and default values
+var { ['a']: foo, ['b']: bar } = { a: 1, b: 2 }; // computed property names
+var { a, b, ...rest } = { a: 1, b: 2, c: 3, d: 4 }; // rest properties (ES2018)
+
+
+/* While / do...while statement */
+
+while (true) something();
+while (1) {
+    something();
+}
+do something(); while (false);
+do {
+    something();
+} while (0);
+
+
+/* With statement */
+
+with (Math) {
+    a = PI * r * r;
+    x = r * cos(PI);
+    y = r * sin(PI / 2);
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]