[gtksourceview: 8/14] javascript.lang, typescript.lang: Better specify object literals/classes



commit 7f07ce6ff1d5db3918c753a8897c8d80bdffadf8
Author: Jeffery To <jeffery to gmail com>
Date:   Sat Jun 6 05:29:18 2020 +0800

    javascript.lang, typescript.lang: Better specify object literals/classes
    
    This refines the highlighting of object literals and class bodies so
    that (some) invalid syntax are not highlighted.
    
    This also fixes the spread syntax in object literals only recognizing an
    identifier instead of an expression (that returns an object to copy
    properties from).

 .../javascript-functions-classes.lang              | 131 ++++----
 data/language-specs/javascript-literals.lang       |  78 ++++-
 data/language-specs/javascript.lang                |  31 +-
 .../typescript-js-functions-classes.lang           | 279 ++++++++++++-----
 data/language-specs/typescript-js-literals.lang    |  17 ++
 .../typescript-type-expressions.lang               |   2 +-
 data/language-specs/typescript.lang                |   8 +-
 tests/syntax-highlighting/file.j                   | 136 +++++++--
 tests/syntax-highlighting/file.js                  | 136 +++++++--
 tests/syntax-highlighting/file.jsx                 | 136 +++++++--
 tests/syntax-highlighting/file.ts                  | 337 +++++++++++++++++----
 tests/syntax-highlighting/file.tsx                 | 337 +++++++++++++++++----
 12 files changed, 1271 insertions(+), 357 deletions(-)
---
diff --git a/data/language-specs/javascript-functions-classes.lang 
b/data/language-specs/javascript-functions-classes.lang
index 8e1307c7..02b34aea 100644
--- a/data/language-specs/javascript-functions-classes.lang
+++ b/data/language-specs/javascript-functions-classes.lang
@@ -227,79 +227,58 @@
          }
     -->
 
-    <context id="_method-definition-modifier" once-only="true">
+    <context id="_property-accessor-keyword" style-ref="js:keyword" once-only="true">
+      <match extended="true">
+        \%{js:get-keyword} | \%{js:set-keyword}
+      </match>
+    </context> <!-- /_property-accessor-keyword -->
+
+    <context id="ordered-property-accessor-keyword" once-only="true">
       <start>\%{js:before-next-token}</start>
       <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="_property-accessor-keyword"/>
+      </include>
+    </context> <!-- /ordered-property-accessor-keyword -->
+
+    <!-- <MethodDefinition> (part of) -->
+    <!-- note that this does not include js-lit:ordered-property-name -->
+    <context id="choice-method-definition" style-ref="js:function-expression" end-parent="true">
+      <start>(?=\()</start>
+      <end>\%{js:before-next-token}</end>
       <include>
         <context ref="js:embedded-lang-hooks"/>
         <context ref="js:comments"/>
 
-        <context id="_method-definition-modifier-content">
+        <context id="_method-definition-content">
           <include>
-
-            <context id="_choice-method-definition-modifier-keyword" style-ref="js:keyword" 
end-parent="true">
-              <start extended="true">
-                (?= \%{js:get-keyword} | \%{js:set-keyword} )
-              </start>
-              <end extended="true">
-                \%{js:get-keyword} | \%{js:set-keyword}
-              </end>
-            </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">
-              <start extended="true">
-                (?=
-                  \%{js:async-keyword}
-                  \%{js:optional-whitespace-or-comments}
-                  (?: \%{js:identifier-start} | \%{js:generator-modifier} )
-                )
-              </start>
-              <end>\%{js:async-keyword}</end>
-            </context> <!-- /_choice-async-method-definition-modifier-keyword -->
-
+            <context ref="_ordered-function-parameters-list"/>
+            <context ref="_last-function-body"/>
           </include>
-        </context> <!-- /_method-definition-modifier-content -->
+        </context> <!-- /_method-definition-content -->
 
       </include>
-    </context> <!-- /_method-definition-modifier -->
+    </context> <!-- /choice-method-definition -->
 
-    <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>
+    <!-- <MethodDefinition> (part of) -->
+    <context id="choice-method-keyword-method-definition" style-ref="js:function-expression" 
end-parent="true">
+      <start>\%{js:method-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="_method-definition-content">
+        <context id="_method-keyword-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"/>
+            <context ref="choice-method-definition"/>
           </include>
-        </context> <!-- /_method-definition-content -->
-
-      </include>
-    </context> <!-- /_method-definition -->
+        </context> <!-- /_method-keyword-method-definition-content -->
 
-    <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 -->
+    </context> <!-- /choice-method-keyword-method-definition -->
 
 
     <!-- # Class expression
@@ -372,34 +351,37 @@
 
     <!-- ## Class body -->
 
-    <context id="_class-body-member-modifier" once-only="true">
+    <context id="_class-body-member-modifier" style-ref="js:keyword" once-only="true">
+      <match>\%{js:static-keyword}</match>
+    </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="_choice-class-body-keyword-named-method-member" style-ref="js:function-expression" 
end-parent="true">
+      <start extended="true">
+        (?: \%{js:get-keyword} | \%{js:set-keyword} | \%{js:static-keyword} )
+        (?= \%{js:optional-whitespace-or-comments} \( )
+      </start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:identifier"/>
         <context ref="js:embedded-lang-hooks"/>
         <context ref="js:comments"/>
 
-        <context id="_class-body-member-modifier-content">
+        <context id="_class-body-keyword-named-method-member-content">
           <include>
-
-            <context id="_choice-class-body-member-modifier-keyword" style-ref="js:keyword" 
end-parent="true">
-              <start>(?=\%{js:static-keyword})</start>
-              <end>\%{js:static-keyword}</end>
-            </context> <!-- /_choice-class-body-member-modifier-keyword -->
-
+            <context ref="choice-method-definition"/>
           </include>
-        </context> <!-- /_class-body-member-modifier-content -->
+        </context> <!-- /_class-body-keyword-named-method-member-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> <!-- /_choice-class-body-keyword-named-method-member -->
 
     <context id="_class-body-members">
       <start>\%{js:before-next-token}</start>
@@ -410,8 +392,17 @@
 
         <context id="_class-body-member-content">
           <include>
+            <!-- try to match before and after modifier -->
+            <context ref="_choice-class-body-keyword-named-method-member"/>
+
             <context ref="_ordered-class-body-member-modifier"/>
-            <context ref="ordered-method-definition"/>
+
+            <context ref="choice-method-keyword-method-definition"/>
+
+            <context ref="ordered-property-accessor-keyword"/>
+            <context ref="js-lit:ordered-property-name"/>
+
+            <context ref="choice-method-definition"/>
           </include>
         </context> <!-- /_class-body-member-content -->
 
diff --git a/data/language-specs/javascript-literals.lang b/data/language-specs/javascript-literals.lang
index 56b11466..cb539c73 100644
--- a/data/language-specs/javascript-literals.lang
+++ b/data/language-specs/javascript-literals.lang
@@ -315,7 +315,8 @@
 
          {
            propertyA: 'a',
-           propertyB: getB(),
+           "propertyB": getB(),
+           1: 'one',
            [ computedName() ]: 2 + 3,
            method() { ... },
            get prop() { return this._prop; },
@@ -324,6 +325,24 @@
          }
     -->
 
+    <!-- ES2018 -->
+    <context id="_choice-object-literal-spread-syntax" end-parent="true">
+      <start>\%{js:spread-syntax}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:spread-syntax"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_object-literal-spread-syntax-content">
+          <include>
+            <context ref="js-expr:expression-without-comma"/>
+          </include>
+        </context> <!-- /_object-literal-spread-syntax-content -->
+
+      </include>
+    </context> <!-- /_choice-object-literal-spread-syntax -->
+
     <context id="_object-literal-property-value" once-only="true">
       <start>:</start>
       <end>\%{js:before-next-token}</end>
@@ -348,12 +367,61 @@
       </include>
     </context> <!-- /_ordered-object-literal-property-value -->
 
+    <context id="_choice-object-literal-keyword-named-property-or-method" end-parent="true">
+      <start extended="true">
+        (?: \%{js:get-keyword} | \%{js:set-keyword} )
+        (?= \%{js:optional-whitespace-or-comments} [:=(] )
+      </start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:identifier"/>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_object-literal-keyword-named-property-or-method-content">
+          <include>
+            <context ref="js-fn:choice-method-definition"/>
+
+            <context ref="_ordered-object-literal-property-value"/>
+            <context ref="js:ordered-default-value-assignment"/> <!-- for destructuring assignment -->
+          </include>
+        </context> <!-- /_object-literal-keyword-named-property-or-method-content -->
+
+      </include>
+    </context> <!-- /_choice-object-literal-keyword-named-property-or-method -->
+
+    <context id="_choice-object-literal-property-or-method" end-parent="true">
+      <start extended="true">
+        (?= [\['"] ) |                         # computed property name, string
+        \%{js:number-start} (?= \.? [0-9] ) |  # number
+        \%{js:identifier-container-start}      # identifier
+      </start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="js:embedded-lang-hooks"/>
+        <context ref="js:comments"/>
+
+        <context id="_object-literal-property-or-method-content">
+          <include>
+            <context ref="js-fn:ordered-property-accessor-keyword"/>
+            <context ref="ordered-property-name"/>
+
+            <context ref="js-fn:choice-method-definition"/>
+
+            <context ref="_ordered-object-literal-property-value"/>
+            <context ref="js:ordered-default-value-assignment"/> <!-- for destructuring assignment -->
+          </include>
+        </context> <!-- /_object-literal-property-or-method-content -->
+
+      </include>
+    </context> <!-- /_choice-object-literal-property-or-method -->
+
     <context id="_object-literal-property-content">
       <include>
-        <context ref="js:ordered-spread-syntax"/> <!-- ES2018 -->
-        <context ref="js-fn:ordered-method-definition"/> <!-- includes property-name -->
-        <context ref="_ordered-object-literal-property-value"/>
-        <context ref="js:ordered-default-value-assignment"/> <!-- for destructuring assignment -->
+        <context ref="_choice-object-literal-spread-syntax"/>
+        <context ref="_choice-object-literal-keyword-named-property-or-method"/>
+        <context ref="js-fn:choice-method-keyword-method-definition"/>
+        <context ref="_choice-object-literal-property-or-method"/>
       </include>
     </context> <!-- /_object-literal-property-content -->
 
diff --git a/data/language-specs/javascript.lang b/data/language-specs/javascript.lang
index eb63b5c6..ef6b4fcd 100644
--- a/data/language-specs/javascript.lang
+++ b/data/language-specs/javascript.lang
@@ -280,6 +280,10 @@
 
     <define-regex id="generator-modifier">\*</define-regex>
 
+    <define-regex id="rest-syntax">\.\.\.</define-regex>
+
+    <define-regex id="spread-syntax">\.\.\.</define-regex>
+
     <!-- "unknown id" errors can occur when using a regex defined in one
           component file in another component file
           https://gitlab.gnome.org/GNOME/gtksourceview/issues/67
@@ -486,13 +490,34 @@
       \%{keyword-start} yield \%{keyword-end}
     </define-regex> <!-- /yield-keyword -->
 
-    <!-- async function (ES2017)
+    <!-- async function: ES2017
          no line terminator allowed between "async" and "function" -->
     <define-regex id="function-expression-keyword" extended="true">
       (?: \%{async-keyword} \%{optional-whitespace-or-comments} )?
       \%{function-keyword}
     </define-regex> <!-- /function-expression-keyword -->
 
+    <!-- async function: ES2017
+         "async" cannot be followed by line terminator -->
+    <define-regex id="method-keyword" extended="true">
+      # get property() / set property()
+      (?: \%{js:get-keyword} | \%{js:set-keyword} )
+      (?=
+        \%{js:optional-whitespace-or-comments}
+        \%{js:identifier-start}
+      ) |
+
+      # async method() / async * generator()
+      \%{js:async-keyword}
+      (?=
+        \%{js:optional-whitespace-or-comments}
+        (?: \%{js:identifier-start} | \%{js:generator-modifier} )
+      ) |
+
+      # * generator()
+      (?= \%{js:generator-modifier} )
+    </define-regex> <!-- /method-keyword -->
+
     <!-- ## Embedded lang hooks
 
          a placeholder context where an embedding language (e.g. html)
@@ -783,7 +808,7 @@
     <!-- ## Misc syntax -->
 
     <context id="_rest-syntax" style-ref="rest-syntax" once-only="true">
-      <match>\.\.\.</match>
+      <match>\%{rest-syntax}</match>
     </context> <!-- /_rest-syntax -->
 
     <context id="ordered-rest-syntax" once-only="true">
@@ -795,7 +820,7 @@
     </context> <!-- /ordered-rest-syntax -->
 
     <context id="_spread-syntax" style-ref="spread-syntax" once-only="true">
-      <match>\.\.\.</match>
+      <match>\%{spread-syntax}</match>
     </context> <!-- /_spread-syntax -->
 
     <context id="ordered-spread-syntax" once-only="true">
diff --git a/data/language-specs/typescript-js-functions-classes.lang 
b/data/language-specs/typescript-js-functions-classes.lang
index c16f4f9c..3fa49727 100644
--- a/data/language-specs/typescript-js-functions-classes.lang
+++ b/data/language-specs/typescript-js-functions-classes.lang
@@ -56,11 +56,6 @@
     <!-- replaces js-fn:_function-parameters-content -->
     <context id="function-parameters-content">
       <include>
-        <!-- decorators are valid for class declaration methods only -->
-        <context ref="typescript:ordered-decorators"/>
-        <!-- accessibility modifiers are valid for class
-             expression/declaration constructors only -->
-        <context ref="_ordered-accessibility-modifier"/>
         <context ref="js:ordered-rest-syntax"/>
         <context ref="js:ordered-binding"/>
         <context ref="typescript:ordered-optional-modifier"/>
@@ -74,19 +69,11 @@
       <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="_function-first-parameter-content">
           <include>
-            <!-- decorators are valid for class declaration methods only -->
-            <context ref="typescript:ordered-decorators"/>
-            <!-- accessibility modifiers are valid for class
-                 expression/declaration constructors only -->
-            <context ref="_ordered-accessibility-modifier"/>
             <context ref="js:ordered-rest-syntax"/>
-            <!-- this parameters are valid for class
-                 expression/declaration methods only -->
             <context ref="typescript:ordered-this-parameter-or-binding"/>
             <context ref="typescript:ordered-optional-modifier"/>
             <context ref="typescript:ordered-type-annotation"/>
@@ -117,55 +104,24 @@
 
     <!-- # Method definition -->
 
-    <!-- ensure type annotation for functions:
-           method(): 'type' { ... }
-         doesn't conflict with object literal property value:
-           property: 'value'
-    -->
-
-    <context id="_function-parameters-list-and-type-annotation" once-only="true">
-      <start>(?=\()</start>
+    <!-- replaces js-fn:choice-method-definition -->
+    <context id="choice-method-definition" style-ref="js:function-expression" end-parent="true">
+      <start>(?=[&lt;(])</start>
       <end>\%{js:before-next-token}</end>
       <include>
         <context ref="js:comments"/>
 
-        <context id="_function-parameters-list-and-type-annotation-content">
+        <context id="_method-definition-content">
           <include>
-            <context ref="js-fn:_ordered-function-parameters-list" style-ref="js:function-expression"/>
+            <context ref="typescript-type-gen:ordered-type-parameters-list"/>
+            <context ref="js-fn:_ordered-function-parameters-list"/>
             <context ref="typescript:ordered-type-annotation"/>
+            <context ref="js-fn:_last-function-body"/>
           </include>
-        </context> <!-- /_function-parameters-list-and-type-annotation-content -->
+        </context> <!-- /_method-definition-content -->
 
       </include>
-    </context> <!-- /_function-parameters-list-and-type-annotation -->
-
-    <context id="_ordered-function-parameters-list-and-type-annotation" once-only="true">
-      <start>\%{js:before-next-token}</start>
-      <end>\%{js:before-next-token}</end>
-      <include>
-        <context ref="_function-parameters-list-and-type-annotation"/>
-      </include>
-    </context> <!-- /_ordered-function-parameters-list-and-type-annotation -->
-
-    <!-- replaces js-fn:_method-definition-content -->
-    <context id="method-definition-content">
-      <include>
-        <context ref="js-fn:_ordered-method-definition-modifier"/>
-        <context ref="js:ordered-generator-modifier"/>
-        <context ref="js-lit:ordered-property-name"/>
-        <!-- technically, only class members can be marked as optional,
-             not object members -->
-        <context ref="typescript:ordered-optional-modifier"/>
-        <!-- technically, only class properties can be marked as
-             definitely assigned, not object members -->
-        <!-- also, optional modifier and definitely assignment
-             assertion cannot both be specified -->
-        <context ref="typescript:ordered-definite-assignment-assertion"/>
-        <context ref="typescript-type-gen:ordered-type-parameters-list"/>
-        <context ref="_ordered-function-parameters-list-and-type-annotation"/>
-        <context ref="js-fn:_last-function-body" style-ref="js:function-expression"/>
-      </include>
-    </context> <!-- /method-definition-content -->
+    </context> <!-- /choice-method-definition -->
 
 
     <!-- # Class expression -->
@@ -249,36 +205,35 @@
 
     <!-- ## Class body -->
 
-    <context id="_class-body-member-modifier" once-only="true">
+    <context id="_class-body-member-modifier" style-ref="js:keyword" once-only="true">
+      <match extended="true">
+        \%{typescript:abstract-keyword} | \%{js:static-keyword}
+      </match>
+    </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="js:comments"/>
-
-        <context id="_class-body-member-modifier-content">
-          <include>
+        <context ref="_class-body-member-modifier"/>
+      </include>
+    </context> <!-- /_ordered-class-body-member-modifier -->
 
-            <context id="_class-body-member-modifier-keywords" style-ref="js:keyword">
-              <match extended="true">
-                \%{typescript:abstract-keyword} |
-                \%{typescript:declare-keyword} |
-                \%{typescript:readonly-keyword}
-              </match>
-            </context> <!-- /_class-body-member-modifier-keywords -->
+    <context id="_class-body-declare-property-modifier" style-ref="js:keyword" once-only="true">
+      <match>\%{typescript:declare-keyword}</match>
+    </context> <!-- /_class-body-declare-property-modifier -->
 
-          </include>
-        </context> <!-- /_class-body-member-modifier-content -->
-
-      </include>
+    <context id="_class-body-readonly-property-modifier" style-ref="js:keyword" once-only="true">
+      <match>\%{typescript:readonly-keyword}</match>
     </context> <!-- /_class-body-member-modifier -->
 
-    <context id="_ordered-class-body-member-modifier" once-only="true">
+    <context id="_ordered-class-body-readonly-property-modifier" once-only="true">
       <start>\%{js:before-next-token}</start>
       <end>\%{js:before-next-token}</end>
       <include>
-        <context ref="_class-body-member-modifier"/>
+        <context ref="_class-body-readonly-property-modifier"/>
       </include>
-    </context> <!-- /_ordered-class-body-member-modifier -->
+    </context> <!-- /_ordered-class-body-readonly-property-modifier -->
 
     <context id="_choice-class-body-index-member" end-parent="true">
       <start>(?=\[)</start>
@@ -296,36 +251,192 @@
       </include>
     </context> <!-- /_choice-class-body-index-member -->
 
-    <context id="_choice-class-body-property-or-method-member" end-parent="true">
+    <context id="_class-body-method-parameters-list" once-only="true">
+      <start>\(</start>
+      <end>\)</end>
+      <include>
+        <context ref="js:comments"/>
+
+        <context id="_class-body-method-parameters-list-content">
+          <include>
+
+            <context id="_class-body-method-first-parameter" once-only="true">
+              <start>\%{js:before-next-token}</start>
+              <end>\%{js:before-next-token}</end>
+              <include>
+                <context ref="js:comments"/>
+
+                <context id="_class-body-method-first-parameter-content">
+                  <include>
+                    <!-- decorators are valid for class declaration
+                         methods only (not class expressions) -->
+                    <context ref="typescript:ordered-decorators"/>
+                    <!-- accessibility modifiers are valid for
+                         constructors only (not other methods) -->
+                    <context ref="_ordered-accessibility-modifier"/>
+                    <context ref="_function-first-parameter-content"/>
+                  </include>
+                </context> <!-- /_class-body-method-first-parameter-content -->
+
+              </include>
+            </context> <!-- /_class-body-method-first-parameter -->
+
+            <context id="_class-body-method-parameters">
+              <start>,</start>
+              <end>\%{js:before-next-token}</end>
+              <include>
+                <context ref="js:comments"/>
+
+                <context id="_class-body-method-parameters-content">
+                  <include>
+                    <!-- decorators are valid for class declaration
+                         methods only (not class expressions) -->
+                    <context ref="typescript:ordered-decorators"/>
+                    <!-- accessibility modifiers are valid for
+                         constructors only (not other methods) -->
+                    <context ref="_ordered-accessibility-modifier"/>
+                    <context ref="function-parameters-content"/>
+                  </include>
+                </context> <!-- /_class-body-method-parameters-content -->
+
+              </include>
+            </context> <!-- /_class-body-method-parameters -->
+
+          </include>
+        </context> <!-- /_class-body-method-parameters-list-content -->
+
+      </include>
+    </context> <!-- /_class-body-method-parameters-list -->
+
+    <context id="_ordered-class-body-method-parameters-list" once-only="true">
       <start>\%{js:before-next-token}</start>
       <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="_class-body-method-parameters-list"/>
+      </include>
+    </context> <!-- /_ordered-class-body-method-parameters-list -->
+
+    <context id="_choice-class-body-method-definition" style-ref="js:function-expression" end-parent="true">
+      <start>(?=[&lt;(])</start>
+      <end>\%{js:before-next-token}</end>
       <include>
         <context ref="js:comments"/>
 
-        <context id="_class-body-property-or-method-member-content">
+        <context id="_class-body-method-definition-content">
           <include>
-            <context ref="js-fn:ordered-method-definition"/>
-            <!-- method-definition has a type-annotation, but it is tied
-                 to function-parameters-list -->
+            <context ref="typescript-type-gen:ordered-type-parameters-list"/>
+            <context ref="_ordered-class-body-method-parameters-list"/>
+            <context ref="typescript:ordered-type-annotation"/>
+            <context ref="js-fn:_last-function-body"/>
+          </include>
+        </context> <!-- /_class-body-method-definition-content -->
+
+      </include>
+    </context> <!-- /_choice-class-body-method-definition -->
+
+    <context id="_choice-class-body-method-keyword-method-definition" style-ref="js:function-expression" 
end-parent="true">
+      <start>\%{js:method-keyword}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:keyword"/>
+        <context ref="js:comments"/>
+
+        <context id="_class-body-method-keyword-method-definition-content">
+          <include>
+            <context ref="js:ordered-generator-modifier"/>
+            <context ref="js-lit:ordered-property-name"/>
+            <context ref="_choice-class-body-method-definition"/>
+          </include>
+        </context> <!-- /_class-body-method-keyword-method-definition-content -->
+
+      </include>
+    </context> <!-- /choice-class-body-method-keyword-method-definition -->
+
+    <context id="_choice-class-body-keyword-named-property-or-method" end-parent="true">
+      <start extended="true">
+        (?:
+          \%{js:get-keyword} |
+          \%{js:set-keyword} |
+          \%{js:static-keyword} |
+          \%{typescript:abstract-keyword} |
+          \%{typescript:declare-keyword} |
+          \%{typescript:private-keyword} |
+          \%{typescript:protected-keyword} |
+          \%{typescript:public-keyword} |
+          \%{typescript:readonly-keyword}
+        )
+        (?= \%{js:optional-whitespace-or-comments} [;?!:=&lt;(] )
+      </start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:identifier"/>
+        <context ref="js:comments"/>
+
+        <context id="_class-body-keyword-named-property-or-method-content">
+          <include>
+            <!-- for class properties and methods -->
+            <context ref="typescript:ordered-optional-modifier"/>
+            <!-- for class properties only -->
+            <context ref="typescript:ordered-definite-assignment-assertion"/>
+            <!-- optional modifier and definitely assignment assertion
+                 cannot both be specified -->
+
+            <context ref="_choice-class-body-method-definition"/>
+
             <context ref="typescript:ordered-type-annotation"/>
             <context ref="js:ordered-default-value-assignment"/>
           </include>
-        </context> <!-- /_class-body-property-or-method-member-content -->
+        </context> <!-- /_class-body-keyword-named-property-or-method-content -->
 
       </include>
-    </context> <!-- /_choice-class-body-property-or-method-member -->
+    </context> <!-- /_choice-class-body-keyword-named-property-or-method -->
+
+    <!-- modifier order:
+         * accessibility ("public" / "protected" / "private")
+         * "abstract" / "static" (mutually exclusive)
+         * "readonly"
+
+         "declare" can be in any position:
+         https://github.com/microsoft/TypeScript/issues/34172
+
+        index members can only be modified with "readonly"
+
+        cannot have abstract generators: abstract *generator()
+        https://github.com/Microsoft/TypeScript/issues/25710
+
+        "abstract async" will probably be not allowed in the future
+        https://github.com/microsoft/TypeScript/issues/28516
+    -->
 
     <!-- replaces js-fn:_class-body-member-content -->
     <context id="class-body-member-content">
       <include>
-        <!-- only "readonly" applies to index members
-             but these all need to be here because of ordering -->
+        <!-- try to match before and after each modifier -->
+        <context ref="_choice-class-body-keyword-named-property-or-method"/>
+
+        <context ref="_class-body-declare-property-modifier"/>
+
         <context ref="_ordered-accessibility-modifier"/>
-        <!-- "static" must precede "readonly" -->
-        <context ref="js-fn:_ordered-class-body-member-modifier"/>
         <context ref="_ordered-class-body-member-modifier"/>
+        <context ref="_ordered-class-body-readonly-property-modifier"/>
+
         <context ref="_choice-class-body-index-member"/>
-        <context ref="_choice-class-body-property-or-method-member"/>
+        <context ref="_choice-class-body-method-keyword-method-definition"/>
+
+        <context ref="js-fn:ordered-property-accessor-keyword"/>
+        <context ref="js-lit:ordered-property-name"/>
+
+        <!-- for class properties and methods -->
+        <context ref="typescript:ordered-optional-modifier"/>
+        <!-- for class properties only -->
+        <context ref="typescript:ordered-definite-assignment-assertion"/>
+        <!-- optional modifier and definitely assignment assertion
+             cannot both be specified -->
+
+        <context ref="_choice-class-body-method-definition"/>
+
+        <context ref="typescript:ordered-type-annotation"/>
+        <context ref="js:ordered-default-value-assignment"/>
       </include>
     </context> <!-- /class-body-member-content -->
 
diff --git a/data/language-specs/typescript-js-literals.lang b/data/language-specs/typescript-js-literals.lang
index 8ef69688..7706037f 100644
--- a/data/language-specs/typescript-js-literals.lang
+++ b/data/language-specs/typescript-js-literals.lang
@@ -111,5 +111,22 @@
       </include>
     </context> <!-- /choice-number -->
 
+
+    <!-- # Object literal -->
+
+    <!-- replaces js-lit:_choice-object-literal-keyword-named-property-or-method -->
+    <context id="choice-object-literal-keyword-named-property-or-method" end-parent="true">
+      <start extended="true">
+        (?: \%{js:get-keyword} | \%{js:set-keyword} )
+        (?= \%{js:optional-whitespace-or-comments} [:=&lt;(] )
+      </start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="js:identifier"/>
+        <context ref="js:comments"/>
+        <context ref="js-lit:_object-literal-keyword-named-property-or-method-content"/>
+      </include>
+    </context> <!-- /choice-object-literal-keyword-named-property-or-method -->
+
   </definitions>
 </language>
diff --git a/data/language-specs/typescript-type-expressions.lang 
b/data/language-specs/typescript-type-expressions.lang
index 6a2f6c15..c44ee241 100644
--- a/data/language-specs/typescript-type-expressions.lang
+++ b/data/language-specs/typescript-type-expressions.lang
@@ -115,7 +115,7 @@
     <!-- this will only catch simple cases :-( -->
     <context id="_choice-type-grouping-this-parameter-or-binding" end-parent="true">
       <start extended="true">
-        (?&lt;= \.\.\. )
+        (?&lt;= \%{js:rest-syntax} )
         (?= \%{js:identifier} ) |  # adding js:optional-whitespace-or-comments in between doesn't work
         (?&lt;! \%{js:identifier-char} )
         (?= \%{js:identifier} \%{js:optional-whitespace-or-comments} [?:,] )
diff --git a/data/language-specs/typescript.lang b/data/language-specs/typescript.lang
index f2fccdff..eec0e772 100644
--- a/data/language-specs/typescript.lang
+++ b/data/language-specs/typescript.lang
@@ -829,8 +829,14 @@
 
     <!-- ## Literals -->
 
+    <!-- ### Number -->
+
     <replace id="js-lit:choice-number" ref="typescript-js-lit:choice-number"/>
 
+    <!-- ### Object literal -->
+
+    <replace id="js-lit:_choice-object-literal-keyword-named-property-or-method" 
ref="typescript-js-lit:choice-object-literal-keyword-named-property-or-method"/>
+
 
     <!-- ## Functions and classes -->
 
@@ -842,7 +848,7 @@
 
     <!-- ### Method definition -->
 
-    <replace id="js-fn:_method-definition-content" ref="typescript-js-fn:method-definition-content"/>
+    <replace id="js-fn:choice-method-definition" ref="typescript-js-fn:choice-method-definition"/>
 
     <!-- ### Class expression -->
 
diff --git a/tests/syntax-highlighting/file.j b/tests/syntax-highlighting/file.j
index b91d7f0c..3910d7c2 100644
--- a/tests/syntax-highlighting/file.j
+++ b/tests/syntax-highlighting/file.j
@@ -396,12 +396,12 @@ var octal2 = 0O4567n;
 
 a = {};
 a = { prop: 'value' };
-a = { prop: 'value', extends: 1 };
+a = { 'prop': 'value', 1: true, .2: 2 };
 
 // Trailing comma
 a = {
     prop: 'value',
-    extends: 1,
+    "extends": 1,
 };
 
 // Shorthand property names
@@ -411,7 +411,17 @@ a = { b, c, d };
 a = {
     _hidden: null,
     get property() { return _hidden; },
-    set property(value) { this._hidden = value; }
+    set property(value) { this._hidden = value; },
+
+    get: 'get',
+    set() { return 'set'; }
+};
+// Incorrectly highlighted as keyword
+a = {
+    get
+    : 'get',
+    set
+    () { return 'set'; }
 };
 
 // Shorthand function notation
@@ -422,9 +432,9 @@ a = {
     // Async function (ES2017)
     async method() {},
     async /* comment */ method() {},
+    async get() {},
     async() {},// method called "async"
     async: false, // property called "async"
-    async prop: 'val', // incorrectly highlighted (syntax error)
 
     // Async generator (ES2018)
     async *generator() {}
@@ -437,7 +447,19 @@ a = {
 };
 
 // Spread properties (ES2018)
-a = { ...b };
+a = {
+    ...b,
+    ...getObj('string')
+};
+
+// Syntax errors
+a = { prop: 'val': 'val' };
+a = { method() {}: 'val' };
+a = { get property() {}: 'val' };
+a = { *generator: 'val' };
+a = { async prop: 'val' };
+a = { ...b: 'val' };
+a = { ...b() { return 'b'; } };
 
 
 /* Regular expression literal */
@@ -689,18 +711,46 @@ a = class Foo {
     *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';
-    }
+    constructor() { this._value = null; }
+
+    get property() { return this._value; }
+    set property(x) { this._value = x; }
+    async method() { return 'async'; }
+    async *generator() { return 'generator'; }
+    static method() { return 'static'; }
+
+    static get property() { return this.staticval; }
+    static set property(x) { this.staticval = x; }
+    static async method() { return 'async'; }
+    static async *generator() { return 'generator'; }
+
+    get() { return this.val; }
+    set(v) { this.val = v; }
+    async() { return 'async'; }
+    static() { return 'static'; }
+
+    static get() { return this.val; }
+    static set(v) { this.val = v; }
+    static async() { return 'async'; }
+    static static() { return 'static'; }
+
+    static
+    static
+    () { return 'static'; }
+};
+// Incorrectly highlighted as keyword
+a = class {
+    get
+    () { return this.val; }
+    set
+    (v) { this.val = v; }
+    static
+    () { return 'static'; }
+    static get
+    () { return this.val; }
+    static
+    set
+    (v) { this.val = v; }
 };
 
 
@@ -919,18 +969,46 @@ class Foo {
     *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';
-    }
+    constructor() { this._value = null; }
+
+    get property() { return this._value; }
+    set property(x) { this._value = x; }
+    async method() { return 'async'; }
+    async *generator() { return 'generator'; }
+    static method() { return 'static'; }
+
+    static get property() { return this.staticval; }
+    static set property(x) { this.staticval = x; }
+    static async method() { return 'async'; }
+    static async *generator() { return 'generator'; }
+
+    get() { return this.val; }
+    set(v) { this.val = v; }
+    async() { return 'async'; }
+    static() { return 'static'; }
+
+    static get() { return this.val; }
+    static set(v) { this.val = v; }
+    static async() { return 'async'; }
+    static static() { return 'static'; }
+
+    static
+    static
+    () { return 'static'; }
+}
+// Incorrectly highlighted as keyword
+class Foo {
+    get
+    () { return this.val; }
+    set
+    (v) { this.val = v; }
+    static
+    () { return 'static'; }
+    static get
+    () { return this.val; }
+    static
+    set
+    (v) { this.val = v; }
 }
 
 
diff --git a/tests/syntax-highlighting/file.js b/tests/syntax-highlighting/file.js
index 9db22959..3fc84f5f 100644
--- a/tests/syntax-highlighting/file.js
+++ b/tests/syntax-highlighting/file.js
@@ -95,12 +95,12 @@ var octal2 = 0O4567n;
 
 a = {};
 a = { prop: 'value' };
-a = { prop: 'value', extends: 1 };
+a = { 'prop': 'value', 1: true, .2: 2 };
 
 // Trailing comma
 a = {
     prop: 'value',
-    extends: 1,
+    "extends": 1,
 };
 
 // Shorthand property names
@@ -110,7 +110,17 @@ a = { b, c, d };
 a = {
     _hidden: null,
     get property() { return _hidden; },
-    set property(value) { this._hidden = value; }
+    set property(value) { this._hidden = value; },
+
+    get: 'get',
+    set() { return 'set'; }
+};
+// Incorrectly highlighted as keyword
+a = {
+    get
+    : 'get',
+    set
+    () { return 'set'; }
 };
 
 // Shorthand function notation
@@ -121,9 +131,9 @@ a = {
     // Async function (ES2017)
     async method() {},
     async /* comment */ method() {},
+    async get() {},
     async() {},// method called "async"
     async: false, // property called "async"
-    async prop: 'val', // incorrectly highlighted (syntax error)
 
     // Async generator (ES2018)
     async *generator() {}
@@ -136,7 +146,19 @@ a = {
 };
 
 // Spread properties (ES2018)
-a = { ...b };
+a = {
+    ...b,
+    ...getObj('string')
+};
+
+// Syntax errors
+a = { prop: 'val': 'val' };
+a = { method() {}: 'val' };
+a = { get property() {}: 'val' };
+a = { *generator: 'val' };
+a = { async prop: 'val' };
+a = { ...b: 'val' };
+a = { ...b() { return 'b'; } };
 
 
 /* Regular expression literal */
@@ -388,18 +410,46 @@ a = class Foo {
     *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';
-    }
+    constructor() { this._value = null; }
+
+    get property() { return this._value; }
+    set property(x) { this._value = x; }
+    async method() { return 'async'; }
+    async *generator() { return 'generator'; }
+    static method() { return 'static'; }
+
+    static get property() { return this.staticval; }
+    static set property(x) { this.staticval = x; }
+    static async method() { return 'async'; }
+    static async *generator() { return 'generator'; }
+
+    get() { return this.val; }
+    set(v) { this.val = v; }
+    async() { return 'async'; }
+    static() { return 'static'; }
+
+    static get() { return this.val; }
+    static set(v) { this.val = v; }
+    static async() { return 'async'; }
+    static static() { return 'static'; }
+
+    static
+    static
+    () { return 'static'; }
+};
+// Incorrectly highlighted as keyword
+a = class {
+    get
+    () { return this.val; }
+    set
+    (v) { this.val = v; }
+    static
+    () { return 'static'; }
+    static get
+    () { return this.val; }
+    static
+    set
+    (v) { this.val = v; }
 };
 
 
@@ -618,18 +668,46 @@ class Foo {
     *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';
-    }
+    constructor() { this._value = null; }
+
+    get property() { return this._value; }
+    set property(x) { this._value = x; }
+    async method() { return 'async'; }
+    async *generator() { return 'generator'; }
+    static method() { return 'static'; }
+
+    static get property() { return this.staticval; }
+    static set property(x) { this.staticval = x; }
+    static async method() { return 'async'; }
+    static async *generator() { return 'generator'; }
+
+    get() { return this.val; }
+    set(v) { this.val = v; }
+    async() { return 'async'; }
+    static() { return 'static'; }
+
+    static get() { return this.val; }
+    static set(v) { this.val = v; }
+    static async() { return 'async'; }
+    static static() { return 'static'; }
+
+    static
+    static
+    () { return 'static'; }
+}
+// Incorrectly highlighted as keyword
+class Foo {
+    get
+    () { return this.val; }
+    set
+    (v) { this.val = v; }
+    static
+    () { return 'static'; }
+    static get
+    () { return this.val; }
+    static
+    set
+    (v) { this.val = v; }
 }
 
 
diff --git a/tests/syntax-highlighting/file.jsx b/tests/syntax-highlighting/file.jsx
index 9506d3ec..2c92b33c 100644
--- a/tests/syntax-highlighting/file.jsx
+++ b/tests/syntax-highlighting/file.jsx
@@ -152,12 +152,12 @@ var octal2 = 0O4567n;
 
 a = {};
 a = { prop: 'value' };
-a = { prop: 'value', extends: 1 };
+a = { 'prop': 'value', 1: true, .2: 2 };
 
 // Trailing comma
 a = {
     prop: 'value',
-    extends: 1,
+    "extends": 1,
 };
 
 // Shorthand property names
@@ -167,7 +167,17 @@ a = { b, c, d };
 a = {
     _hidden: null,
     get property() { return _hidden; },
-    set property(value) { this._hidden = value; }
+    set property(value) { this._hidden = value; },
+
+    get: 'get',
+    set() { return 'set'; }
+};
+// Incorrectly highlighted as keyword
+a = {
+    get
+    : 'get',
+    set
+    () { return 'set'; }
 };
 
 // Shorthand function notation
@@ -178,9 +188,9 @@ a = {
     // Async function (ES2017)
     async method() {},
     async /* comment */ method() {},
+    async get() {},
     async() {},// method called "async"
     async: false, // property called "async"
-    async prop: 'val', // incorrectly highlighted (syntax error)
 
     // Async generator (ES2018)
     async *generator() {}
@@ -193,7 +203,19 @@ a = {
 };
 
 // Spread properties (ES2018)
-a = { ...b };
+a = {
+    ...b,
+    ...getObj('string')
+};
+
+// Syntax errors
+a = { prop: 'val': 'val' };
+a = { method() {}: 'val' };
+a = { get property() {}: 'val' };
+a = { *generator: 'val' };
+a = { async prop: 'val' };
+a = { ...b: 'val' };
+a = { ...b() { return 'b'; } };
 
 
 /* Regular expression literal */
@@ -445,18 +467,46 @@ a = class Foo {
     *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';
-    }
+    constructor() { this._value = null; }
+
+    get property() { return this._value; }
+    set property(x) { this._value = x; }
+    async method() { return 'async'; }
+    async *generator() { return 'generator'; }
+    static method() { return 'static'; }
+
+    static get property() { return this.staticval; }
+    static set property(x) { this.staticval = x; }
+    static async method() { return 'async'; }
+    static async *generator() { return 'generator'; }
+
+    get() { return this.val; }
+    set(v) { this.val = v; }
+    async() { return 'async'; }
+    static() { return 'static'; }
+
+    static get() { return this.val; }
+    static set(v) { this.val = v; }
+    static async() { return 'async'; }
+    static static() { return 'static'; }
+
+    static
+    static
+    () { return 'static'; }
+};
+// Incorrectly highlighted as keyword
+a = class {
+    get
+    () { return this.val; }
+    set
+    (v) { this.val = v; }
+    static
+    () { return 'static'; }
+    static get
+    () { return this.val; }
+    static
+    set
+    (v) { this.val = v; }
 };
 
 
@@ -675,18 +725,46 @@ class Foo {
     *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';
-    }
+    constructor() { this._value = null; }
+
+    get property() { return this._value; }
+    set property(x) { this._value = x; }
+    async method() { return 'async'; }
+    async *generator() { return 'generator'; }
+    static method() { return 'static'; }
+
+    static get property() { return this.staticval; }
+    static set property(x) { this.staticval = x; }
+    static async method() { return 'async'; }
+    static async *generator() { return 'generator'; }
+
+    get() { return this.val; }
+    set(v) { this.val = v; }
+    async() { return 'async'; }
+    static() { return 'static'; }
+
+    static get() { return this.val; }
+    static set(v) { this.val = v; }
+    static async() { return 'async'; }
+    static static() { return 'static'; }
+
+    static
+    static
+    () { return 'static'; }
+}
+// Incorrectly highlighted as keyword
+class Foo {
+    get
+    () { return this.val; }
+    set
+    (v) { this.val = v; }
+    static
+    () { return 'static'; }
+    static get
+    () { return this.val; }
+    static
+    set
+    (v) { this.val = v; }
 }
 
 
diff --git a/tests/syntax-highlighting/file.ts b/tests/syntax-highlighting/file.ts
index 8f30877b..ff86f798 100644
--- a/tests/syntax-highlighting/file.ts
+++ b/tests/syntax-highlighting/file.ts
@@ -401,32 +401,48 @@ let hex_bigint = 0XFF_0C_00_FFn;
 
 /* Object literal */
 
+// Property value vs type annotation
 a = {
-    // Property value vs type annotation 
     property: void 1,
-    method(): void {},
+    method(): void {}
+};
 
-    // Type parameters, type annotations for methods
+// Type annotations for methods
+a = {
     method(this: void, x: number, y?: string, z: number = 0, ...rest: any[]): void {},
-    method<T extends Function>(x: T): T {},
     get property(): string {},
     set property(value: string) {}
 };
 
+// Type parameters for methods
+a = {
+    method<T extends Function>(x: T): T {},
+    get<T extends Function>(x: T): T {}
+};
+// Incorrectly highlighted as keyword
+a = {
+    get
+    <T>(x: T): T {}
+};
+
 
 /* Function expression / declaration */
 
-// Type parameters, type annotations
+// Type annotations
 a = function (this: void, x: number, y?: string, z: number = 1, ...rest: any[]): void {};
-a = function <T extends Function>(x: T): T {};
 function fn(this: void, x: number, y?: string, z: number = 1, ...rest: any[]): void {}
+
+// Type parameters
+a = function <T extends Function>(x: T): T {};
 function fn<T extends Function>(x: T): T {}
 
 
 /* Grouping / arrow function parameters */
 
-// Type parameters, type annotations
+// Type annotations
 a = (x: number, y?: string, z: number = 0, ...rest: any[]): void => x + y;
+
+// Type parameters
 a = <T extends Function>(x: T): T => x;
 
 
@@ -454,32 +470,169 @@ a = class extends Super implements Super.Sub {};
 class MyClass implements Super.Sub {}
 class MyClass extends Super implements Super.Sub {}
 
+// Class properties
 a = class {
-    // Class property
     property;
-    public property?: number;
-    private property!: number;
-    protected static readonly property: number = 1;
-    abstract property;
-    declare property: number; // for useDefineForClassFields
+    property = 1;
+};
 
-    // Accessibility modifiers, type annotation, parameter properties for constructor
-    private constructor(public x: number, private y?: string);
-    protected constructor(protected x: number = 1) {}
+// Type annotation
+a = class {
+    property: number;
+    property: number = 1;
 
-    // Accessibility modifiers, type parameters, type annotation for class method
-    public method?(this: void, x: number, y?: string, z: number = 1, ...rest: any[]): void;
-    private static method<T extends Function>(x: T): T {}
-    public abstract method();
-    protected get property(): string {}
-    static set property(value: string) {}
-    abstract get property(): string {}
+    constructor(x: number, y?: string) {}
+    constructor(x: number = 1) {}
 
-    // Index members
+    method(this: void, x: number, y?: string, z: number = 1, ...rest: any[]): void {}
+    get property(): string {}
+    set property(value: string) {}
+};
+
+// Index members
+a = class {
     [index: number]: string;
+};
+
+// Accessibility modifiers for properties / methods
+a = class {
+    public property;
+    protected method() {}
+    private get property(): string {}
+};
+
+// Abstract properties / methods
+a = abstract class {
+    abstract property;
+    abstract method(): void;
+};
+
+// Read-only properties / index members
+a = class {
+    readonly property;
     readonly [prop: string]: any;
 };
 
+// "declare" properties (no emit when useDefineForClassFields)
+a = class {
+    declare property;
+};
+
+// Optional properties / methods
+a = class {
+    property?;
+    method?(): void;
+};
+
+// Definite assignment assertion for properties
+a = class {
+    property!;
+    property!: number;
+};
+
+// Class method type parameters
+a = class {
+    method<T extends Function>(x: T): T {}
+};
+
+// Parameter properties for constructor
+a = class {
+    constructor(public x: number, private y?: string) {}
+    constructor(protected x: number = 1) {}
+};
+
+// Multiple modifiers
+a = class {
+    public static readonly property;
+    private abstract method();
+
+    // "declare" can be in any position
+    declare protected abstract readonly property;
+    protected declare abstract readonly property;
+    protected abstract declare readonly property;
+    protected abstract readonly declare property;
+};
+
+// Modifier-named properties / methods
+a = class {
+    public;
+    declare protected?;
+    private!;
+    public: number;
+    protected = 1;
+    private() {}
+    public<T>(x: T): T { return x; }
+
+    abstract;
+    public abstract?;
+    protected abstract!;
+    private abstract: number;
+    declare abstract = 1;
+    protected abstract() {}
+    private abstract<T>(x: T): T { return x; }
+
+    readonly;
+    public readonly?;
+    static readonly!;
+    abstract readonly: number;
+    declare readonly = 1;
+    protected readonly() {}
+    private readonly<T>(x: T): T { return x; }
+
+    declare;
+    static declare?;
+    abstract declare!;
+    readonly declare: number;
+    declare declare = 1;
+    public declare() {}
+    protected declare<T>(x: T): T { return x; }
+};
+
+// Syntax errors
+// Multiple accessibility modifiers
+a = class { public private property; };
+a = class { private protected property; };
+a = class { protected public property; };
+// "static" and "abstract" both present
+a = class { static abstract property; };
+a = class { abstract static property; };
+// "static" / "abstract" before accessibility modifier
+a = class { static private property; };
+a = class { abstract protected property; };
+// "readonly" before accessibility modifier
+a = class { readonly public property; };
+// "readonly" before "static" / "abstract"
+a = class { readonly static property; };
+a = class { readonly abstract property; };
+
+// Incorrectly highlighted
+// Property/method name highlighted as keyword
+a = class {
+    public
+    ;
+    declare private
+    ?;
+    abstract readonly
+    : number;
+    readonly declare
+    = 1;
+    protected abstract
+    () {}
+};
+// Abstract generators not allowed
+abstract class Foo {
+    abstract *generator(): object;
+}
+// Modifiers other than "readonly" do not apply to index members
+a = class {
+    public [prop: string]: any;
+    protected [prop: string]: any;
+    private [prop: string]: any;
+    static [prop: string]: any;
+    abstract [prop: string]: any;
+    declare [prop: string]: any;
+};
+
 
 /* Expression */
 
@@ -687,12 +840,12 @@ var octal2 = 0O4567n;
 
 a = {};
 a = { prop: 'value' };
-a = { prop: 'value', extends: 1 };
+a = { 'prop': 'value', 1: true, .2: 2 };
 
 // Trailing comma
 a = {
     prop: 'value',
-    extends: 1,
+    "extends": 1,
 };
 
 // Shorthand property names
@@ -702,7 +855,17 @@ a = { b, c, d };
 a = {
     _hidden: null,
     get property() { return _hidden; },
-    set property(value) { this._hidden = value; }
+    set property(value) { this._hidden = value; },
+
+    get: 'get',
+    set() { return 'set'; }
+};
+// Incorrectly highlighted as keyword
+a = {
+    get
+    : 'get',
+    set
+    () { return 'set'; }
 };
 
 // Shorthand function notation
@@ -713,9 +876,9 @@ a = {
     // Async function (ES2017)
     async method() {},
     async /* comment */ method() {},
+    async get() {},
     async() {},// method called "async"
     async: false, // property called "async"
-    async prop: 'val', // incorrectly highlighted (syntax error)
 
     // Async generator (ES2018)
     async *generator() {}
@@ -728,7 +891,19 @@ a = {
 };
 
 // Spread properties (ES2018)
-a = { ...b };
+a = {
+    ...b,
+    ...getObj('string')
+};
+
+// Syntax errors
+a = { prop: 'val': 'val' };
+a = { method() {}: 'val' };
+a = { get property() {}: 'val' };
+a = { *generator: 'val' };
+a = { async prop: 'val' };
+a = { ...b: 'val' };
+a = { ...b() { return 'b'; } };
 
 
 /* Regular expression literal */
@@ -980,18 +1155,46 @@ a = class Foo {
     *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';
-    }
+    constructor() { this._value = null; }
+
+    get property() { return this._value; }
+    set property(x) { this._value = x; }
+    async method() { return 'async'; }
+    async *generator() { return 'generator'; }
+    static method() { return 'static'; }
+
+    static get property() { return this.staticval; }
+    static set property(x) { this.staticval = x; }
+    static async method() { return 'async'; }
+    static async *generator() { return 'generator'; }
+
+    get() { return this.val; }
+    set(v) { this.val = v; }
+    async() { return 'async'; }
+    static() { return 'static'; }
+
+    static get() { return this.val; }
+    static set(v) { this.val = v; }
+    static async() { return 'async'; }
+    static static() { return 'static'; }
+
+    static
+    static
+    () { return 'static'; }
+};
+// Incorrectly highlighted as keyword
+a = class {
+    get
+    () { return this.val; }
+    set
+    (v) { this.val = v; }
+    static
+    () { return 'static'; }
+    static get
+    () { return this.val; }
+    static
+    set
+    (v) { this.val = v; }
 };
 
 
@@ -1210,18 +1413,46 @@ class Foo {
     *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';
-    }
+    constructor() { this._value = null; }
+
+    get property() { return this._value; }
+    set property(x) { this._value = x; }
+    async method() { return 'async'; }
+    async *generator() { return 'generator'; }
+    static method() { return 'static'; }
+
+    static get property() { return this.staticval; }
+    static set property(x) { this.staticval = x; }
+    static async method() { return 'async'; }
+    static async *generator() { return 'generator'; }
+
+    get() { return this.val; }
+    set(v) { this.val = v; }
+    async() { return 'async'; }
+    static() { return 'static'; }
+
+    static get() { return this.val; }
+    static set(v) { this.val = v; }
+    static async() { return 'async'; }
+    static static() { return 'static'; }
+
+    static
+    static
+    () { return 'static'; }
+}
+// Incorrectly highlighted as keyword
+class Foo {
+    get
+    () { return this.val; }
+    set
+    (v) { this.val = v; }
+    static
+    () { return 'static'; }
+    static get
+    () { return this.val; }
+    static
+    set
+    (v) { this.val = v; }
 }
 
 
diff --git a/tests/syntax-highlighting/file.tsx b/tests/syntax-highlighting/file.tsx
index fd13e4dc..7d9589c2 100644
--- a/tests/syntax-highlighting/file.tsx
+++ b/tests/syntax-highlighting/file.tsx
@@ -507,32 +507,48 @@ let hex_bigint = 0XFF_0C_00_FFn;
 
 /* Object literal */
 
+// Property value vs type annotation
 a = {
-    // Property value vs type annotation 
     property: void 1,
-    method(): void {},
+    method(): void {}
+};
 
-    // Type parameters, type annotations for methods
+// Type annotations for methods
+a = {
     method(this: void, x: number, y?: string, z: number = 0, ...rest: any[]): void {},
-    method<T extends Function>(x: T): T {},
     get property(): string {},
     set property(value: string) {}
 };
 
+// Type parameters for methods
+a = {
+    method<T extends Function>(x: T): T {},
+    get<T extends Function>(x: T): T {}
+};
+// Incorrectly highlighted as keyword
+a = {
+    get
+    <T>(x: T): T {}
+};
+
 
 /* Function expression / declaration */
 
-// Type parameters, type annotations
+// Type annotations
 a = function (this: void, x: number, y?: string, z: number = 1, ...rest: any[]): void {};
-a = function <T extends Function>(x: T): T {};
 function fn(this: void, x: number, y?: string, z: number = 1, ...rest: any[]): void {}
+
+// Type parameters
+a = function <T extends Function>(x: T): T {};
 function fn<T extends Function>(x: T): T {}
 
 
 /* Grouping / arrow function parameters */
 
-// Type parameters, type annotations
+// Type annotations
 a = (x: number, y?: string, z: number = 0, ...rest: any[]): void => x + y;
+
+// Type parameters
 a = <T extends Function>(x: T): T => x;
 
 
@@ -560,32 +576,169 @@ a = class extends Super implements Super.Sub {};
 class MyClass implements Super.Sub {}
 class MyClass extends Super implements Super.Sub {}
 
+// Class properties
 a = class {
-    // Class property
     property;
-    public property?: number;
-    private property!: number;
-    protected static readonly property: number = 1;
-    abstract property;
-    declare property: number; // for useDefineForClassFields
+    property = 1;
+};
 
-    // Accessibility modifiers, type annotation, parameter properties for constructor
-    private constructor(public x: number, private y?: string);
-    protected constructor(protected x: number = 1) {}
+// Type annotation
+a = class {
+    property: number;
+    property: number = 1;
 
-    // Accessibility modifiers, type parameters, type annotation for class method
-    public method?(this: void, x: number, y?: string, z: number = 1, ...rest: any[]): void;
-    private static method<T extends Function>(x: T): T {}
-    public abstract method();
-    protected get property(): string {}
-    static set property(value: string) {}
-    abstract get property(): string {}
+    constructor(x: number, y?: string) {}
+    constructor(x: number = 1) {}
 
-    // Index members
+    method(this: void, x: number, y?: string, z: number = 1, ...rest: any[]): void {}
+    get property(): string {}
+    set property(value: string) {}
+};
+
+// Index members
+a = class {
     [index: number]: string;
+};
+
+// Accessibility modifiers for properties / methods
+a = class {
+    public property;
+    protected method() {}
+    private get property(): string {}
+};
+
+// Abstract properties / methods
+a = abstract class {
+    abstract property;
+    abstract method(): void;
+};
+
+// Read-only properties / index members
+a = class {
+    readonly property;
     readonly [prop: string]: any;
 };
 
+// "declare" properties (no emit when useDefineForClassFields)
+a = class {
+    declare property;
+};
+
+// Optional properties / methods
+a = class {
+    property?;
+    method?(): void;
+};
+
+// Definite assignment assertion for properties
+a = class {
+    property!;
+    property!: number;
+};
+
+// Class method type parameters
+a = class {
+    method<T extends Function>(x: T): T {}
+};
+
+// Parameter properties for constructor
+a = class {
+    constructor(public x: number, private y?: string) {}
+    constructor(protected x: number = 1) {}
+};
+
+// Multiple modifiers
+a = class {
+    public static readonly property;
+    private abstract method();
+
+    // "declare" can be in any position
+    declare protected abstract readonly property;
+    protected declare abstract readonly property;
+    protected abstract declare readonly property;
+    protected abstract readonly declare property;
+};
+
+// Modifier-named properties / methods
+a = class {
+    public;
+    declare protected?;
+    private!;
+    public: number;
+    protected = 1;
+    private() {}
+    public<T>(x: T): T { return x; }
+
+    abstract;
+    public abstract?;
+    protected abstract!;
+    private abstract: number;
+    declare abstract = 1;
+    protected abstract() {}
+    private abstract<T>(x: T): T { return x; }
+
+    readonly;
+    public readonly?;
+    static readonly!;
+    abstract readonly: number;
+    declare readonly = 1;
+    protected readonly() {}
+    private readonly<T>(x: T): T { return x; }
+
+    declare;
+    static declare?;
+    abstract declare!;
+    readonly declare: number;
+    declare declare = 1;
+    public declare() {}
+    protected declare<T>(x: T): T { return x; }
+};
+
+// Syntax errors
+// Multiple accessibility modifiers
+a = class { public private property; };
+a = class { private protected property; };
+a = class { protected public property; };
+// "static" and "abstract" both present
+a = class { static abstract property; };
+a = class { abstract static property; };
+// "static" / "abstract" before accessibility modifier
+a = class { static private property; };
+a = class { abstract protected property; };
+// "readonly" before accessibility modifier
+a = class { readonly public property; };
+// "readonly" before "static" / "abstract"
+a = class { readonly static property; };
+a = class { readonly abstract property; };
+
+// Incorrectly highlighted
+// Property/method name highlighted as keyword
+a = class {
+    public
+    ;
+    declare private
+    ?;
+    abstract readonly
+    : number;
+    readonly declare
+    = 1;
+    protected abstract
+    () {}
+};
+// Abstract generators not allowed
+abstract class Foo {
+    abstract *generator(): object;
+}
+// Modifiers other than "readonly" do not apply to index members
+a = class {
+    public [prop: string]: any;
+    protected [prop: string]: any;
+    private [prop: string]: any;
+    static [prop: string]: any;
+    abstract [prop: string]: any;
+    declare [prop: string]: any;
+};
+
 
 /* Expression */
 
@@ -795,12 +948,12 @@ var octal2 = 0O4567n;
 
 a = {};
 a = { prop: 'value' };
-a = { prop: 'value', extends: 1 };
+a = { 'prop': 'value', 1: true, .2: 2 };
 
 // Trailing comma
 a = {
     prop: 'value',
-    extends: 1,
+    "extends": 1,
 };
 
 // Shorthand property names
@@ -810,7 +963,17 @@ a = { b, c, d };
 a = {
     _hidden: null,
     get property() { return _hidden; },
-    set property(value) { this._hidden = value; }
+    set property(value) { this._hidden = value; },
+
+    get: 'get',
+    set() { return 'set'; }
+};
+// Incorrectly highlighted as keyword
+a = {
+    get
+    : 'get',
+    set
+    () { return 'set'; }
 };
 
 // Shorthand function notation
@@ -821,9 +984,9 @@ a = {
     // Async function (ES2017)
     async method() {},
     async /* comment */ method() {},
+    async get() {},
     async() {},// method called "async"
     async: false, // property called "async"
-    async prop: 'val', // incorrectly highlighted (syntax error)
 
     // Async generator (ES2018)
     async *generator() {}
@@ -836,7 +999,19 @@ a = {
 };
 
 // Spread properties (ES2018)
-a = { ...b };
+a = {
+    ...b,
+    ...getObj('string')
+};
+
+// Syntax errors
+a = { prop: 'val': 'val' };
+a = { method() {}: 'val' };
+a = { get property() {}: 'val' };
+a = { *generator: 'val' };
+a = { async prop: 'val' };
+a = { ...b: 'val' };
+a = { ...b() { return 'b'; } };
 
 
 /* Regular expression literal */
@@ -1088,18 +1263,46 @@ a = class Foo {
     *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';
-    }
+    constructor() { this._value = null; }
+
+    get property() { return this._value; }
+    set property(x) { this._value = x; }
+    async method() { return 'async'; }
+    async *generator() { return 'generator'; }
+    static method() { return 'static'; }
+
+    static get property() { return this.staticval; }
+    static set property(x) { this.staticval = x; }
+    static async method() { return 'async'; }
+    static async *generator() { return 'generator'; }
+
+    get() { return this.val; }
+    set(v) { this.val = v; }
+    async() { return 'async'; }
+    static() { return 'static'; }
+
+    static get() { return this.val; }
+    static set(v) { this.val = v; }
+    static async() { return 'async'; }
+    static static() { return 'static'; }
+
+    static
+    static
+    () { return 'static'; }
+};
+// Incorrectly highlighted as keyword
+a = class {
+    get
+    () { return this.val; }
+    set
+    (v) { this.val = v; }
+    static
+    () { return 'static'; }
+    static get
+    () { return this.val; }
+    static
+    set
+    (v) { this.val = v; }
 };
 
 
@@ -1318,18 +1521,46 @@ class Foo {
     *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';
-    }
+    constructor() { this._value = null; }
+
+    get property() { return this._value; }
+    set property(x) { this._value = x; }
+    async method() { return 'async'; }
+    async *generator() { return 'generator'; }
+    static method() { return 'static'; }
+
+    static get property() { return this.staticval; }
+    static set property(x) { this.staticval = x; }
+    static async method() { return 'async'; }
+    static async *generator() { return 'generator'; }
+
+    get() { return this.val; }
+    set(v) { this.val = v; }
+    async() { return 'async'; }
+    static() { return 'static'; }
+
+    static get() { return this.val; }
+    static set(v) { this.val = v; }
+    static async() { return 'async'; }
+    static static() { return 'static'; }
+
+    static
+    static
+    () { return 'static'; }
+}
+// Incorrectly highlighted as keyword
+class Foo {
+    get
+    () { return this.val; }
+    set
+    (v) { this.val = v; }
+    static
+    () { return 'static'; }
+    static get
+    () { return this.val; }
+    static
+    set
+    (v) { this.val = v; }
 }
 
 


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