[gtksourceview: 3/6] jsx.lang: Add language definition



commit c1239bd6bdd8c723397930c2b48e2b7e10e58cc1
Author: Jeffery To <jeffery to gmail com>
Date:   Thu Oct 10 23:04:51 2019 +0800

    jsx.lang: Add language definition

 data/language-specs/jsx.lang       | 375 ++++++++++++++++
 po/POTFILES.skip                   |   1 +
 tests/syntax-highlighting/file.jsx | 869 +++++++++++++++++++++++++++++++++++++
 3 files changed, 1245 insertions(+)
---
diff --git a/data/language-specs/jsx.lang b/data/language-specs/jsx.lang
new file mode 100644
index 00000000..4184ddb7
--- /dev/null
+++ b/data/language-specs/jsx.lang
@@ -0,0 +1,375 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ This file is part of GtkSourceView
+
+ Author: 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
+ 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="jsx" name="JSX" version="2.0" _section="Script">
+  <metadata>
+    <property name="mimetypes">application/jsx;application/x-jsx;text/x-jsx;text/jsx</property>
+    <property name="globs">*.jsx</property>
+    <property name="line-comment-start">//</property>
+    <property name="block-comment-start">/*</property>
+    <property name="block-comment-end">*/</property>
+  </metadata>
+
+  <styles>
+    <style id="element"               name="Element"/>
+    <style id="spread-attribute"      name="Spread attribute"/>
+    <style id="attribute-expression"  name="Attribute expression"/>
+    <style id="child-expression"      name="Child expression"/>
+  </styles>
+
+  <keyword-char-class>[a-zA-Z0-9_$-]</keyword-char-class>
+
+  <definitions>
+
+    <!-- Based on the JSX spec: https://github.com/facebook/jsx -->
+
+
+    <!-- using a style from another lang file before referencing a
+         context from the file causes "style not defined" errors
+    -->
+    <context id="style-not-defined-error-workaround">
+      <include>
+        <context ref="xml:xml"/>
+      </include>
+    </context> <!-- /style-not-defined-error-workaround -->
+
+
+    <!-- # General -->
+
+    <!-- reference js here to avoid "unknown id" error when referencing
+         a regex from js before referencing a context from js
+    -->
+    <context id="main-lang">
+      <include>
+        <context ref="js:js"/>
+      </include>
+    </context> <!-- /main-lang -->
+
+    <define-regex id="_start-tag-start">(?:&lt;(?!/))</define-regex>
+
+
+    <!-- # Names -->
+
+    <define-regex id="_identifier-part" extended="true">
+      (?: \%{js:identifier-part} | - )
+    </define-regex> <!-- /_identifier-part -->
+
+    <!-- <JSXIdentifier> -->
+    <define-regex id="_identifier" extended="true">
+      \%{js:identifier-start} \%{_identifier-part}*
+    </define-regex> <!-- /_identifier -->
+
+    <context id="_identifier" once-only="true">
+      <match>\%{_identifier}</match>
+    </context> <!-- /_identifier -->
+
+    <context id="_choice-identifier" end-parent="true">
+      <start>(?=\%{js:identifier-start})</start>
+      <end>\%{_identifier}</end>
+    </context> <!-- /_choice-identifier -->
+
+    <!-- <JSXNamespacedName> -->
+    <define-regex id="_namespaced-name" extended="true">
+      (?: \%{_identifier} : \%{_identifier} )
+    </define-regex> <!-- /_namespaced-name -->
+
+    <context id="_choice-namespaced-name" end-parent="true">
+      <start>(?=\%{_namespaced-name})</start>
+      <end>\%{_namespaced-name}</end>
+    </context> <!-- /_choice-namespaced-name -->
+
+    <!-- <JSXMemberExpression> -->
+    <define-regex id="_member-expression" extended="true">
+      (?: \%{_identifier} (?: \. \%{_identifier} )+ )
+    </define-regex> <!-- /_member-expression -->
+
+    <context id="_choice-member-expression" end-parent="true">
+      <start>(?=\%{_member-expression})</start>
+      <end>\%{_member-expression}</end>
+    </context> <!-- /_choice-member-expression -->
+
+
+    <!-- # Element name -->
+
+    <!-- <JSXElementName> -->
+    <context id="_element-name" style-ref="xml:element-name" once-only="true">
+      <start>(?=\%{js:identifier-start})</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="js:comments"/>
+
+        <context id="_element-name-content">
+          <include>
+            <context ref="_choice-namespaced-name"/>
+            <context ref="_choice-member-expression"/>
+            <context ref="_choice-identifier"/>
+          </include>
+        </context> <!-- /_element-name-content -->
+
+      </include>
+    </context> <!-- /_element-name -->
+
+    <context id="_ordered-element-name" once-only="true">
+      <start>\%{js:before-next-token}</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="_element-name"/>
+      </include>
+    </context> <!-- /_ordered-element-name -->
+
+
+    <!-- # Attributes -->
+
+    <!-- ## Spread attributes -->
+
+    <!-- <JSXSpreadAttribute> -->
+    <context id="_spread-attributes" style-ref="spread-attribute">
+      <start>{</start>
+      <end>}</end>
+      <include>
+        <context ref="js:comments"/>
+
+        <context id="_spread-attribute-content">
+          <include>
+            <context ref="js:ordered-spread-syntax"/>
+            <context ref="js-expr:expression-without-comma"/>
+          </include>
+        </context> <!-- /_spread-attribute-content -->
+
+      </include>
+    </context> <!-- /_spread-attributes -->
+
+    <!-- ## Attribute name -->
+
+    <!-- <JSXAttributeName> -->
+    <context id="_attribute-names" style-ref="xml:attribute-name">
+      <start>(?=\%{js:identifier-start})</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context ref="js:comments"/>
+
+        <context id="_attribute-name-content">
+          <include>
+            <context ref="_choice-namespaced-name"/>
+            <context ref="_choice-identifier"/>
+          </include>
+        </context> <!-- /_attribute-name-content -->
+
+      </include>
+    </context> <!-- /_attribute-names -->
+
+    <!-- ## Attribute value -->
+
+    <!-- <JSXAttributeValue> (part of) -->
+    <context id="_choice-attribute-value-string" style-ref="xml:attribute-value" end-parent="true" 
class="string" class-disabled="no-spell-check">
+      <start>["']</start>
+      <end>\%{0@start}</end>
+      <include>
+        <!-- no comments here -->
+
+        <context id="_attribute-value-string-content">
+          <include>
+            <!-- javascript escapes do not appear to be parsed here
+                 but xml entities / character references appear to be
+                 parsed -->
+            <context ref="xml:entity"/>
+            <context ref="xml:character-reference"/>
+          </include>
+        </context> <!-- /_attribute-value-string-content -->
+
+      </include>
+    </context> <!-- /_choice-attribute-value-string -->
+
+    <!-- <JSXAttributeValue> (part of) -->
+    <context id="_choice-attribute-value-expression" style-ref="attribute-expression" end-parent="true">
+      <start>{</start>
+      <end>}</end>
+      <include>
+        <context ref="js:comments"/>
+
+        <context id="_attribute-value-expression-content">
+          <include>
+            <!-- no spread syntax here -->
+            <context ref="js-expr:expression-without-comma"/>
+          </include>
+        </context> <!-- /_attribute-value-expression-content -->
+
+      </include>
+    </context> <!-- /_choice-attribute-value-expression -->
+
+    <!-- ## Attribute initializer -->
+
+    <!-- <JSXAttributeInitializer> -->
+    <context id="_attribute-initializers">
+      <start>=</start>
+      <end>\%{js:before-next-token}</end>
+      <include>
+        <context sub-pattern="0" where="start" style-ref="xml:attribute-name"/>
+        <context ref="js:comments"/>
+
+        <context id="_attribute-initializer-content">
+          <include>
+            <context ref="_choice-attribute-value-string"/>
+            <context ref="_choice-attribute-value-expression"/>
+            <context ref="choice-element"/>
+          </include>
+        </context> <!-- /_attribute-initializer-content -->
+
+      </include>
+    </context> <!-- /_attribute-initializers -->
+
+
+    <!-- # Element / fragment -->
+
+    <context id="_element-content">
+      <include>
+
+        <!-- ## Start tag -->
+
+        <!-- <JSXOpeningElement> / <JSXSelfClosingElement> -->
+        <context id="_start-tag-head" style-ref="xml:tag" once-only="true" class="no-spell-check">
+          <start>\%{_start-tag-start}</start>
+          <end>(?=/?&gt;)</end>
+          <include>
+            <context sub-pattern="0" where="start" style-ref="xml:element-name"/>
+            <context ref="js:comments"/>
+
+            <context id="_start-tag-head-content">
+              <include>
+                <context ref="_ordered-element-name"/>
+                <context ref="_spread-attributes"/>
+                <context ref="_attribute-names"/>
+                <context ref="_attribute-initializers"/>
+              </include>
+            </context> <!-- /_start-tag-head-content -->
+
+          </include>
+        </context> <!-- /_start-tag-head -->
+
+        <context id="_start-tag-tail-empty-element-end-parent" style-ref="xml:tag" end-parent="true" 
class="no-spell-check">
+          <start>(?=/&gt;)</start>
+          <end>/&gt;</end>
+          <include>
+            <context sub-pattern="0" where="end" style-ref="xml:element-name"/>
+          </include>
+        </context> <!-- /_start-tag-tail-empty-element-end-parent -->
+
+        <context id="_start-tag-tail" style-ref="xml:tag" once-only="true" class="no-spell-check">
+          <match>&gt;</match>
+          <include>
+            <context sub-pattern="0" style-ref="xml:element-name"/>
+          </include>
+        </context> <!-- /_start-tag-tail -->
+
+        <!-- ## End tag -->
+
+        <context id="_end-tag-end-parent" style-ref="xml:tag" end-parent="true" class="no-spell-check">
+          <start>&lt;/</start>
+          <end>&gt;</end>
+          <include>
+            <context sub-pattern="0" where="start" style-ref="xml:element-name"/>
+            <context sub-pattern="0" where="end" style-ref="xml:element-name"/>
+            <context ref="js:comments"/>
+
+            <context id="_end-tag-content">
+              <include>
+                <context ref="_ordered-element-name"/>
+              </include>
+            </context> <!-- /_end-tag-content -->
+
+          </include>
+        </context> <!-- /_end-tag-end-parent -->
+
+        <!-- ## Nested elements -->
+
+        <!-- <JSXElement> / <JSXFragment> -->
+        <context id="_elements" style-ref="element" class-disabled="no-spell-check">
+          <start>(?=\%{_start-tag-start})</start>
+          <include>
+            <!-- no comments here - comments appear to be parsed as regular
+                 text -->
+            <context ref="_element-content"/>
+          </include>
+        </context> <!-- /_elements -->
+
+        <!-- ## Child expressions -->
+
+        <!-- <JSXChild> / <JSXChildExpression> -->
+        <context id="_child-expressions" style-ref="child-expression" class="no-spell-check">
+          <start>{</start>
+          <end>}</end>
+          <include>
+            <context ref="js:comments"/>
+
+            <context id="_child-expression-content">
+              <include>
+                <context ref="js:ordered-spread-syntax"/>
+                <context ref="js-expr:expression-without-comma"/>
+              </include>
+            </context> <!-- /_child-expression-content -->
+
+          </include>
+        </context> <!-- /_child-expressions -->
+
+        <!-- ## XML syntax -->
+
+        <!-- javascript escapes do not appear to be parsed here
+             but xml character entity / numeric character references
+             appear to be parsed -->
+        <context ref="xml:entity"/>
+        <context ref="xml:character-reference"/>
+      </include>
+    </context> <!-- /_element-content -->
+
+    <!-- <JSXElement> / <JSXFragment> -->
+    <context id="choice-element" style-ref="element" end-parent="true" class-disabled="no-spell-check">
+      <start>(?=\%{_start-tag-start})</start>
+      <include>
+        <!-- no comments here - comments appear to be parsed as regular
+             text -->
+        <context ref="_element-content"/>
+      </include>
+    </context> <!-- /choice-element -->
+
+
+    <!-- # Primary expression -->
+
+    <context id="_jsx-primary-expression-content">
+      <include>
+        <context ref="choice-element"/>
+        <context ref="js-expr:_primary-expression-content" original="true"/>
+      </include>
+    </context> <!-- /_jsx-primary-expression-content -->
+
+    <replace id="js-expr:_primary-expression-content" ref="_jsx-primary-expression-content"/>
+
+
+    <!-- # Main context -->
+
+    <context id="jsx" class="no-spell-check">
+      <include>
+        <context ref="main-lang"/>
+      </include>
+    </context> <!-- /jsx -->
+
+  </definitions>
+</language>
diff --git a/po/POTFILES.skip b/po/POTFILES.skip
index f633f842..fe78eebb 100644
--- a/po/POTFILES.skip
+++ b/po/POTFILES.skip
@@ -77,6 +77,7 @@ 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/jsx.lang
 data/language-specs/julia.lang
 data/language-specs/kotlin.lang
 data/language-specs/latex.lang
diff --git a/tests/syntax-highlighting/file.jsx b/tests/syntax-highlighting/file.jsx
new file mode 100644
index 00000000..941c41f5
--- /dev/null
+++ b/tests/syntax-highlighting/file.jsx
@@ -0,0 +1,869 @@
+/*
+ * JSX Elements
+ */
+
+// Element name
+( <div></div> );
+( <my-custom-component></my-custom-component> );
+( <namespace:component></namespace:component> );
+( <Module.Sub.Component></Module.Sub.Component> );
+
+// Attributes
+( <div {...props}></div> ); // spread attributes
+( <div class="main"></div> );
+( <namespace:component namespace:attribute='value'></namespace:component> );
+( <div class={classes[0]}></div> );
+
+// Empty element
+( <img /> );
+
+// Nested elements
+(
+    <div>
+        <span></span>
+        <img />
+    </div>
+);
+
+// Child expression
+(
+    <div>
+        {["1", 2, three].join('+')}
+        {...obj}
+    </div>
+);
+
+// XML character entity / numeric character references
+( <div>&gt;&#47;</div> );
+
+// Fragment
+(
+    <>
+        <div>
+             <img />
+        </div>
+        <div>
+            <span></span>
+        </div>
+    </>
+);
+
+
+// from file.js
+
+/*
+ * 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/m, /def/u ];
+a = { regex: /abc/s }; // s (dotAll): ES2018
+(1 === 0) ? /abc/ : /def/;
+/abc/; /* Comment */
+/abc/; // Comment
+var matches = /abc/.exec('Alphabet ... that should contain abc, right?');
+
+// 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]"
+
+// Control escape
+/\cJ/;
+
+// Unicode property escape (ES2018)
+/\p{General_Category=Letter}/u;
+/\p{Letter}/u;
+
+// Named capture groups (ES2018)
+/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
+/(?<foobar>foo|bar)/u;
+/^(?<half>.*).\k<half>$/u; // backreference
+
+/* 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)}`;
+
+
+/*
+ * 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]