diff --git a/autotests/folding/test.ts.fold b/autotests/folding/test.ts.fold index f1bf22d..b31e88d 100644 --- a/autotests/folding/test.ts.fold +++ b/autotests/folding/test.ts.fold @@ -1,225 +1,280 @@ /// // TypeScript Test class Student { fullName: string; constructor(public firstName: string, public middleInitial: string, public lastName: string) { this.fullName = firstName + " " + middleInitial + " " + lastName; } } interface Person { firstName: string; lastName: string; } function greeter(person : Person) { return "Hello, " + person.firstName + " " + person.lastName; } let user = new Student("Jane", "M.", "User"); // Built-in document.body.innerHTML = greeter(user); to JSON.stringify() console.log("Hello world"); const os = require('os'); Math.LOG10E; Number.MAX_SAFE_INTEGER; import http = require("http"); import path = require("path"); import URL = url.URL; import { Readable, Writable } from "stream"; import { isBuffer, isString, isObject } from "util"; const port = 8__88___8; const baseUrl = new URL(`http://localhost:${port}/`); const rootDir = path.dirname(__dirname); const defaultBrowser = os.platform() === "win32" ? "edge" : "chrome"; let browser: "edge" | "chrome" | "none" = defaultBrowser; let grep: string | undefined; interface FileBasedTestConfiguration { [setting: string]: string; } function swapCase(s: string): string { return s.replace(/\w/g, (ch) => { const up = ch.toUpperCase(); return ch === up ? ch.toLowerCase() : up; }); } for (var i in pieces) { switch (true) { case /^\"?Accession\"?/.test(pieces[i]): numeration[0] = i; break; } } const enum ResponseCode { Success = 200, BadRequest = 400 } // Substitutions export function isInOpenInterval(start: number, end: number) { return tersible(a => a > start && a < end, () => `(${start}...${end})`) } const bar = `${x} ${y}`; // readonly function f1(mt: [number, number], rt: readonly [number, number]) { } function f2(ma: string[], ra: readonly string[], mt: [string, string], rt: readonly [string, string]) { } type ReadWrite = { -readonly [P in keyof T] : T[P] }; // const assertion let obj = { x: 10, y: [20, 30], z: { a: { b: 42 } } } as const; let r2 = { x: 20, y: 10 } as const; let r2 = {...p} as const; let p1 = { x: 10, y: 20 as const }; let p2 = { 10 as const, 'foo' as const }; // Definite assignment operator class Cl { one?: string; two!: string; } let x! = 1; // Function with multiple arguments const children = [].map>>(element => { if (!this.identityProvider) { return element; } return element; }); // Numerics var a = 0xA; var b = 0b1; var c = 0o7; var d = 1.1E+3; var e = 1.E+3; var f = .1E+3; var g = 1E+3; var h = 1.1; var i = 1.; var j = .1; var k = 1; var l = 1__.e+3_22 | .2____e2 | 0o1_23 | 11__. ; // Bigint const binBig = 0b101n; const octBig = 0o567n; const hexBig = 0xC0Bn; const decBig = 123n; // Types let a: null = null; let b: number = 123; let c: number = 123.456; let d: string = `Geeks`; let e: undefined = undefined; let f: boolean = true; let g: number = 0b111001; // Binary let h: number = 0o436; // Octal let i: number = 0xadf0d; // Hexa-Decimal let j: bigint = 123n; const query = query<[number], number>(` SELECT * FROM statistics WHERE unit_id = $1`) function runQuery() { const query = createQuery<[number[]], Table>(` some SQL here `) return database.execute(query) } aa: string ? string string // Don't highlight aa: string assa | string string ; string aa: { string string } // Don't highlight aa: [ string string ] aa: ( string string ) // Don't highlight aa: string interface a { aa: /* comment */ string, bb: /* comment */ number, cc: // comment void, dd: any, } null, <{[key]: () => any}> null null, <{[key]: () =>{a: number}}> null // Correctly highlighting regular expressions dt = ((dt[0] * 1e9 + dt[1]) / 1e6).toFixed(3).replace(/\.?0+$/, '') + 'ms'; (a[0] / 2) / (2) // Type guards function isNumber(x: any): x is number { return typeof x === "number"; } // Conditional expressions (highlight keywords before ":") class C { w = () => this.a() ? true : this.b() ? false : true; z = () => this.b() ? hello : k; } function foo(arg: T): T extends B ? number : string { if (arg === "A") return 111; return "returning a string"; } +let x = (foo === null || foo === undefined) ? + undefined : + foo.bar.baz(); +let y = (foo === null || foo === undefined) ? /* comment */ undefined : + foo.bar.baz(); + +// Operator ?? +let x = foo ?? bar(); + // Types and logical `&&` and `||` operators after `as` expression Date as any || null; //Assertions const assert: (value: unknown) => asserts value = value => {} declare function assertIsString(value: unknown): asserts value is string; declare function assertIsArrayOfStrings(value: unknown): asserts value is string[]; declare function assertDefined(value: T): asserts value is NonNullable; namespace Debug { export declare function assert(value: unknown, message?: string): asserts value; export declare function assertDefined(value: T): asserts value is NonNullable; } // Only highlight valid regular expressions, of a single line, after strings // See: https://github.com/microsoft/TypeScript-TmLanguage/issues/786 "text" /No-Regex "text" /Regex[:)]*/; const a = "6" / 2; /*comment*/ const b = 5; console.log("4" / "2"); // 2 // Single quote const a = '6' / 2; /*comment*/ const b = 5; console.log('4' / '2'); // 2 // Template const a = `6` / 2; /*comment*/ const b = 5; console.log(`4` / `2`); // 2 + + [1, 2, 3].reduce[]>((previous) => previous, []); + +// Multiline arrow assignment +const h = { + setSubProperty: < + T extends A, + K extends keyof T, + J extends keyof T[K] + >( + property: K, + key: J, + value: T[K][J] + ) => {} +} + +// Private-Named Instance Fields +class Greeter { + #name: string; + constructor(name: string) { + this.#name = name; + } + greet() { + console.log(`hello ${this.#name}`); + } +} +class Test { + #NAME1: () => string; + prop1 = x.#NAMEabc.#NAMEabc; + prop2 = x.#NAMEabc.#NAMEabc(); + prop3 = this.#NAMEabc.#NAME(); +} + +// Type-only imports and exports +import type T from "mod"; +import type { U, V } from "mod"; +import type * as types from "mod"; +import type from "mod"; +import type = require("mod"); +import type T = require("mod"); + +export { a as b, x }; +export * from "AnotherModule"; +export { k as m, l } from "AnotherModule"; +export type { T }; +export type { U } from "AnotherModule"; diff --git a/autotests/folding/test.tsx.fold b/autotests/folding/test.tsx.fold index c9a5d67..b10e0d2 100644 --- a/autotests/folding/test.tsx.fold +++ b/autotests/folding/test.tsx.fold @@ -1,153 +1,158 @@ // TypeScript React /** @author Willy * @url https://reactjs.org/ **/ import React from 'react'; import { PhotoStory, VideoStory } from './stories'; function Story(props) { const SpecificStory = components[props.storyType]; return story={ props.story } attr2="&ref;" attr3="Hello\n" />; } function attr1={ /> function return class var 0x123 { } &noRef; hello() React.Component() } attr2="&ref;"> /* no comment*/ function /> return class var 0x123 &ref; hello() React.Component() ./> anyWord /> { function return class var 0x123 hello() React.Component() } > > > > > > > /*comment*/attr1/*comment*/= /*comment*/"value"/*comment*/attr2 /*comment*/attr3='a' key/*comment*/key2 /> // Detect Valid tags /* comment */ >> { /* comment */ /> word . } return /* comment multiline */ /> /* comment */ /> && /*comment*/ /> & /*comment*/ /> { >Hello> } ? />; [ /> ( /> ,/> =/> &&/> ||/> return /> ; default/> ; > > /> > return > anyWord anyWord/*comment*/ . & | % /* comment*/ // TODO: Fix this (comment before the tag name): var x = </**/div>; // Tag after ":" annotation: /> annotation: text [ /> ] console.log("hello") // Type assertion in tag /> > > /> // Non-ASCII tag name & attribute <日本語>>; 本本:本-本 aa本:本 aa:aa /> /> />; { ... x } y ={2 } z />; let k1 = a={10} b="hi" {...o} > hi hi hi! >; let k2 = a={10} b="hi"> > My Div > {(name: string) => > My name {name} >} >; let k3 = initialValues={{ x: "y" }} nextValues={a => ({ x: a.x })} />; // No Error // OK let k1 = a={10} b="hi"><> /> />>; let k2 = a={10} b="hi"><> /> />>; let k3 = a={10} b="hi"><> /> />>; let k4 = a={10} b="hi"><> /> />>; // OK let k1 = > > Hello > > world >>; let k2 = > > Hello > {(user: any) => >{user.name}>}>; let k3 = > {1} {"That is a number"} >; let k4 = > > Hello > >; // Empty tags hello<> hello <>; // no whitespace < >; // lots of whitespace < /*starting wrap*/ >/*ending wrap*/>; // comments in the tags <>hi; // text inside <>>hi>>bye>; // children <>>1><>>2.1>>2.2>>3>; // nested fragments <>#; // # would cause scanning error if not in jsxtext // Tags after substitutions in templates `aaa${>>//comment /*comment*//>}` // Don't highlight tags within type declaration type T12 = ReturnType<(() => T)>; type T13 = ReturnType<([]>() => T)>; type T14 = ReturnType; type T15 = ReturnType<(s: string) => void>; // Don't highlight tags within variable declaration let myIdentity: (arg: T) => T = />; var myIdentity: (arg: U) => U = identity; const myIdentity: {(arg: T): T} = identity; // Don't highlight tags within interfaces and classes interface GenericIdentityFn { (arg: T): T; } class Handler { info: (arg: T): T ; > > } // Highlight "( arg: T ) => { const a = arg return a } > text extends I/> > // Here "" is a tag // Check character after tag name, do not highlight invalid tags /* comment */ ? /*comment*/# + +// Conditionals expressions +let y = foo == null ? null : />; +let x = (foo === null || foo === undefined) ? + undefined :>hello>; diff --git a/autotests/html/test.ts.html b/autotests/html/test.ts.html index 13d57c7..e8c88f6 100644 --- a/autotests/html/test.ts.html +++ b/autotests/html/test.ts.html @@ -1,232 +1,287 @@ test.ts
 /// <reference types="node" />
 
 // TypeScript Test
 
 class Student {
     fullName: string;
     constructor(public firstName: string, public middleInitial: string, public lastName: string) {
         this.fullName = firstName + " " + middleInitial + " " + lastName;
     }
 }
 
 interface Person {
     firstName: string;
     lastName: string;
 }
 
 function greeter(person : Person) {
     return "Hello, " + person.firstName + " " + person.lastName;
 }
 
 let user = new Student("Jane", "M.", "User");
 
 // Built-in
 document.body.innerHTML = greeter(user); to
 JSON.stringify()
 console.log("Hello world");
 const os = require('os');
 Math.LOG10E;
 Number.MAX_SAFE_INTEGER;
 
 import http = require("http");
 import path = require("path");
 import URL = url.URL;
 import { Readable, Writable } from "stream";
 import { isBuffer, isString, isObject } from "util";
 
 const port = 8__88___8;
 const baseUrl = new URL(`http://localhost:${port}/`);
 const rootDir = path.dirname(__dirname);
 
 const defaultBrowser = os.platform() === "win32" ? "edge" : "chrome";
 let browser: "edge" | "chrome" | "none" = defaultBrowser;
 let grep: string | undefined;
 
 interface FileBasedTestConfiguration {
     [setting: string]: string;
 }
 
 function swapCase(s: string): string {
     return s.replace(/\w/g, (ch) => {
         const up = ch.toUpperCase();
         return ch === up ? ch.toLowerCase() : up;
     });
 }
 
 for (var i in pieces) {
     switch (true) {
         case /^\"?Accession\"?/.test(pieces[i]):
             numeration[0] = i;
             break;
     }
 }
 
 const enum ResponseCode {
     Success = 200,
     BadRequest = 400
 }
 
 // Substitutions
 export function isInOpenInterval(start: number, end: number) {
     return tersible(a => a > start && a < end, () => `(${start}...${end})`)
 }
 const bar = `${x} ${y}`;
 
 // readonly
 function f1(mt: [number, number], rt: readonly [number, number]) {
 }
 
 function f2(ma: string[], ra: readonly string[], mt: [string, string], rt: readonly [string, string]) {
 }
 
 type ReadWrite<T> = { -readonly [P in keyof T] : T[P] };
 
 // const assertion
 let obj = { x: 10, y: [20, 30], z: { a: { b: 42 } } } as const;
 let r2 = { x: 20, y: 10 } as const;
 let r2 = {...p} as const;
 let p1 = { x: 10, y: 20 as const };
 let p2 = { 10 as const, 'foo' as const };
 
 // Definite assignment operator
 class Cl {
     one?: string;
     two!: string;
 }
 let x! = 1;
 
 // Function with multiple arguments
 const children = [].map<Set<Map<number, string>>>(element => {
     if (!this.identityProvider) {
         return element;
     }
     return element;
 });
 
 // Numerics
 var a = 0xA;
 var b = 0b1;
 var c = 0o7;
 var d = 1.1E+3;
 var e = 1.E+3;
 var f = .1E+3;
 var g = 1E+3;
 var h = 1.1;
 var i = 1.;
 var j = .1;
 var k =  1;
 var l = 1__.e+3_22 | .2____e2 | 0o1_23 | 11__. ;
 
 // Bigint
 const binBig = 0b101n;
 const octBig = 0o567n;
 const hexBig = 0xC0Bn;
 const decBig = 123n;
 
 // Types
 let a: null = null;
 let b: number = 123;
 let c: number = 123.456;
 let d: string = `Geeks`;
 let e: undefined = undefined;
 let f: boolean = true;
 let g: number = 0b111001; // Binary
 let h: number = 0o436; // Octal
 let i: number = 0xadf0d; // Hexa-Decimal
 let j: bigint = 123n;
 
 const query = query<[number], number>(`
         SELECT *
         FROM statistics
         WHERE unit_id = $1`)
 
 function runQuery() {
     const query = createQuery<[number[]], Table<Columns>>(`
         some SQL here
     `)
     return database.execute(query)
 }
 
 aa: <sdf/> string ?<ssd/> string
  string // Don't highlight
 aa: string assa |
    string
    string ;
    string
 
 aa: { string
     string } // Don't highlight
 
 aa: [ string
       string ]
 aa: ( string
       string ) // Don't highlight
 aa: string <string>
 interface a {
     aa: /* comment
         */ string,
     bb: /* comment */
         number,
     cc: // comment
         void,
     dd:
         any,
 }
 
 null, <{[key]: () => any}> null
 null, <{[key]: () =>{a: number}}> null
 
 // Correctly highlighting regular expressions
 dt = ((dt[0] * 1e9 + dt[1]) / 1e6).toFixed(3).replace(/\.?0+$/, '') + 'ms';
 (a[0] / 2) / (2)
 
 // Type guards
 function isNumber(x: any): x is number {
     return typeof x === "number";
 }
 
 // Conditional expressions (highlight keywords before ":")
 class C {
     w = () =>
         this.a() ? true : this.b() ? false : true;
     z = () =>
         this.b() ? hello : k;
 }
 
 function foo<T extends X>(arg: T): T extends B ? number : string {
   if (arg === "A") return <T extends B ? number : never>111;
   return <T extends B ? never : string>"returning a string";
 }
 
+let x = (foo === null || foo === undefined) ?
+    undefined :
+    foo.bar.baz();
+let y = (foo === null || foo === undefined) ? /* comment */ undefined :
+    foo.bar.baz();
+
+// Operator ??
+let x = foo ?? bar();
+
 // Types and logical `&&` and `||` operators after `as` expression
 Date as any || null;
 
 //Assertions
 const assert: (value: unknown) => asserts value = value => {}
-declare function assertIsString(value: unknown): asserts value is string;
-declare function assertIsArrayOfStrings(value: unknown): asserts value is string[];
-declare function assertDefined<T>(value: T): asserts value is NonNullable<T>;
+declare function assertIsString(value: unknown): asserts value is string;
+declare function assertIsArrayOfStrings(value: unknown): asserts value is string[];
+declare function assertDefined<T>(value: T): asserts value is NonNullable<T>;
 namespace Debug {
-    export declare function assert(value: unknown, message?: string): asserts value;
-    export declare function assertDefined<T>(value: T): asserts value is NonNullable<T>;
+    export declare function assert(value: unknown, message?: string): asserts value;
+    export declare function assertDefined<T>(value: T): asserts value is NonNullable<T>;
 }
 
 // Only highlight valid regular expressions, of a single line, after strings
 // See: https://github.com/microsoft/TypeScript-TmLanguage/issues/786
 "text" /No-Regex
 "text" /Regex[:)]*/;
 const a = "6" / 2; /*comment*/ const b = 5;
 console.log("4" / "2"); // 2
 // Single quote
 const a = '6' / 2; /*comment*/ const b = 5;
 console.log('4' / '2'); // 2
 // Template
 const a = `6` / 2; /*comment*/ const b = 5;
 console.log(`4` / `2`); // 2
+
+ [1, 2, 3].reduce<readonly number[]>((previous) => previous, []);
+
+// Multiline arrow assignment
+const h = {
+  setSubProperty: <
+      T extends A,
+      K extends keyof T,
+      J extends keyof T[K]
+    >(
+      property: K,
+      key: J,
+      value: T[K][J]
+    ) =>  {}
+}
+
+// Private-Named Instance Fields
+class Greeter {
+    #name: string;
+    constructor(name: string) {
+        this.#name = name;
+    }
+    greet() {
+        console.log(`hello ${this.#name}`);
+    }
+}
+class Test {
+    #NAME1: () => string;
+    prop1 = x.#NAMEabc.#NAMEabc;
+    prop2 = x.#NAMEabc.#NAMEabc();
+    prop3 = this.#NAMEabc.#NAME();
+}
+
+// Type-only imports and exports
+import type T from "mod";
+import type { U, V } from "mod";
+import type * as types from "mod";
+import type from "mod";
+import type = require("mod");
+import type T = require("mod");
+
+export { a as b, x };
+export * from "AnotherModule";
+export { k as m, l } from "AnotherModule";
+export type { T };
+export type { U } from "AnotherModule";
 
diff --git a/autotests/html/test.tsx.html b/autotests/html/test.tsx.html index e8557c8..b8008a4 100644 --- a/autotests/html/test.tsx.html +++ b/autotests/html/test.tsx.html @@ -1,160 +1,165 @@ test.tsx
 // TypeScript React
 
 /** @author Willy <willy@wmail.com>
   * @url https://reactjs.org/ **/
 
 import React from 'react';
 import { PhotoStory, VideoStory } from './stories';
 
 function Story(props) {
   const SpecificStory = components[props.storyType];
   return <SpecificStory story={ props.story } attr2="&ref;" attr3="Hello\n" />;
 }
 
 function
 <Tag attr1={ <tag/> function <noTag/> return class var 0x123 { } &noRef; hello() React.Component() } attr2="&ref;">
 	/* no comment*/ function <tag/> return class var 0x123 &ref; hello() React.Component()
 	.<tag/> anyWord <tag/>
 	{ function <tag> return class var 0x123 hello() React.Component() }
 </Tag>
 
 <tag1> </tag1>
 <tag1> </Tag$>
 <Tag$> </tag>
 
 <tag/*comment*/attr1/*comment*/= /*comment*/"value"/*comment*/attr2 /*comment*/attr3='a' key/*comment*/key2 />
 
 // Detect Valid tags
 
 /* comment */ <tag></tag>
 { /* comment
 	*/ <Tag />
 	word <noTag/> . <noTag/> } <noTag/>
 return /* comment
 	multiline */ <tag/> /* comment */ <Tag/>
 && /*comment*/ <Tag/>
 & /*comment*/ <noTag/>
 
   <tag/>
 { <hello>Hello</hello> }
 ?<Tag />;
 [ <tag />  ( <tag />
 ,<Tag/>    =<Tag/>
 &&<tag/>   ||<tag/>
 return <tag/> ;
 default<tag/> ;
 <Tag> <tag> <tag$/> </tag> return </Tag>
 
 anyWord<noTag>
 anyWord/*comment*/ <noTag/>
 .<noTag>
 &<notag> | <noTag/>
 % /* comment*/ <noTag/>
 
 // TODO: Fix this (comment before the tag name):
 var x = </**/div></div>;
 
 // Tag after ":"
 annotation: <tag/>
 annotation: text [ <tag/> ]
 console.log("hello")
 
 // Type assertion in tag
 <C<number>/>
 <C<number>> </C>
 <C
 <error />
 
 // Non-ASCII tag name & attribute
 <日本語></日本語>;
 <Component 本本:本-本 aa本:本 aa:aa />
 <aaaa:ñ />
 
 <Namespace.DeepNamespace.Component />;
 <Component { ... x } y
 ={2 } z />;
 
 let k1 =
     <Comp a={10} b="hi" {...o} >
         hi hi hi!
     </Comp>;
 
 let k2 =
     <Comp a={10} b="hi">
         <div> My Div </div>
         {(name: string) => <div> My name {name} </div>}
     </Comp>;
 
 let k3 = <GenericComponent initialValues={{ x: "y" }} nextValues={a => ({ x: a.x })} />; // No Error
 
 // OK
 let k1 = <Comp a={10} b="hi"><></><Button /><AnotherButton /></Comp>;
 let k2 = <Comp a={10} b="hi"><><Button /></><AnotherButton /></Comp>;
 let k3 = <Comp a={10} b="hi"><><Button /><AnotherButton /></></Comp>;
 let k4 = <SingleChildComp a={10} b="hi"><><Button /><AnotherButton /></></SingleChildComp>;
 // OK
 let k1 = <div> <h2> Hello </h2> <h1> world </h1></div>;
 let k2 = <div> <h2> Hello </h2> {(user: any) => <h2>{user.name}</h2>}</div>;
 let k3 = <div> {1} {"That is a number"} </div>;
 let k4 = <Button> <h2> Hello </h2> </Button>;
 
 // Empty tags
 hello<>
 hello<string>
 
 <></>; // no whitespace
 <    ></   >; // lots of whitespace
 < /*starting wrap*/ ></ /*ending wrap*/>; // comments in the tags
 <>hi</>; // text inside
 <><span>hi</span><div>bye</div></>; // children
 <><span>1</span><><span>2.1</span><span>2.2</span></><span>3</span></>; // nested fragments
 <>#</>; // # would cause scanning error if not in jsxtext
 
 // Tags after substitutions in templates
 `aaa${<tag></tag>//comment
     /*comment*/<A/>}`
 
 // Don't highlight tags within type declaration
 type T12 = ReturnType<(<T>() => T)>;
 type T13 = ReturnType<(<T extends U, U extends number[]>() => T)>;
 type T14 = ReturnType<typeof f1>;
 type T15 = ReturnType<(s: string) => void>;
 
 // Don't highlight tags within variable declaration
 let myIdentity: <T>(arg: T) => T <noTag/> = <Tag />;
 var myIdentity: <U>(arg: U) => U = identity;
 const myIdentity: {<T>(arg: T): T} = identity;
 
 // Don't highlight tags within interfaces and classes
 interface GenericIdentityFn {
     <T>(arg: T): T;
     <noTag />
 }
 class Handler {
     info: <T>(arg: T): T <noTag />;
     <tag> </tag>
 }
 
 // Highlight "<T extends" as a type assertion, not as a tag
 // (the "cast" expression works and isn't supported in the TSX file)
 const goodHighlighting = <T extends I>(
   arg: T
 ) => {
   const a = arg
   return a
 }
 <tag> text <T extends I/> </tag> // Here "<T extends I/>" is a tag
 
 // Check character after tag name, do not highlight invalid tags
 <noTag  ?
 <noTag  ,
 <noTag /* comment */ ?
 <noTag#
 <noTag/*comment*/#
+
+// Conditionals expressions
+let y = foo == null ? null : <Tag/>;
+let x = (foo === null || foo === undefined) ?
+    undefined :<tag>hello</tag>;
 
diff --git a/autotests/input/test.ts b/autotests/input/test.ts index 0d385a1..53e94f0 100644 --- a/autotests/input/test.ts +++ b/autotests/input/test.ts @@ -1,225 +1,280 @@ /// // TypeScript Test class Student { fullName: string; constructor(public firstName: string, public middleInitial: string, public lastName: string) { this.fullName = firstName + " " + middleInitial + " " + lastName; } } interface Person { firstName: string; lastName: string; } function greeter(person : Person) { return "Hello, " + person.firstName + " " + person.lastName; } let user = new Student("Jane", "M.", "User"); // Built-in document.body.innerHTML = greeter(user); to JSON.stringify() console.log("Hello world"); const os = require('os'); Math.LOG10E; Number.MAX_SAFE_INTEGER; import http = require("http"); import path = require("path"); import URL = url.URL; import { Readable, Writable } from "stream"; import { isBuffer, isString, isObject } from "util"; const port = 8__88___8; const baseUrl = new URL(`http://localhost:${port}/`); const rootDir = path.dirname(__dirname); const defaultBrowser = os.platform() === "win32" ? "edge" : "chrome"; let browser: "edge" | "chrome" | "none" = defaultBrowser; let grep: string | undefined; interface FileBasedTestConfiguration { [setting: string]: string; } function swapCase(s: string): string { return s.replace(/\w/g, (ch) => { const up = ch.toUpperCase(); return ch === up ? ch.toLowerCase() : up; }); } for (var i in pieces) { switch (true) { case /^\"?Accession\"?/.test(pieces[i]): numeration[0] = i; break; } } const enum ResponseCode { Success = 200, BadRequest = 400 } // Substitutions export function isInOpenInterval(start: number, end: number) { return tersible(a => a > start && a < end, () => `(${start}...${end})`) } const bar = `${x} ${y}`; // readonly function f1(mt: [number, number], rt: readonly [number, number]) { } function f2(ma: string[], ra: readonly string[], mt: [string, string], rt: readonly [string, string]) { } type ReadWrite = { -readonly [P in keyof T] : T[P] }; // const assertion let obj = { x: 10, y: [20, 30], z: { a: { b: 42 } } } as const; let r2 = { x: 20, y: 10 } as const; let r2 = {...p} as const; let p1 = { x: 10, y: 20 as const }; let p2 = { 10 as const, 'foo' as const }; // Definite assignment operator class Cl { one?: string; two!: string; } let x! = 1; // Function with multiple arguments const children = [].map>>(element => { if (!this.identityProvider) { return element; } return element; }); // Numerics var a = 0xA; var b = 0b1; var c = 0o7; var d = 1.1E+3; var e = 1.E+3; var f = .1E+3; var g = 1E+3; var h = 1.1; var i = 1.; var j = .1; var k = 1; var l = 1__.e+3_22 | .2____e2 | 0o1_23 | 11__. ; // Bigint const binBig = 0b101n; const octBig = 0o567n; const hexBig = 0xC0Bn; const decBig = 123n; // Types let a: null = null; let b: number = 123; let c: number = 123.456; let d: string = `Geeks`; let e: undefined = undefined; let f: boolean = true; let g: number = 0b111001; // Binary let h: number = 0o436; // Octal let i: number = 0xadf0d; // Hexa-Decimal let j: bigint = 123n; const query = query<[number], number>(` SELECT * FROM statistics WHERE unit_id = $1`) function runQuery() { const query = createQuery<[number[]], Table>(` some SQL here `) return database.execute(query) } aa: string ? string string // Don't highlight aa: string assa | string string ; string aa: { string string } // Don't highlight aa: [ string string ] aa: ( string string ) // Don't highlight aa: string interface a { aa: /* comment */ string, bb: /* comment */ number, cc: // comment void, dd: any, } null, <{[key]: () => any}> null null, <{[key]: () =>{a: number}}> null // Correctly highlighting regular expressions dt = ((dt[0] * 1e9 + dt[1]) / 1e6).toFixed(3).replace(/\.?0+$/, '') + 'ms'; (a[0] / 2) / (2) // Type guards function isNumber(x: any): x is number { return typeof x === "number"; } // Conditional expressions (highlight keywords before ":") class C { w = () => this.a() ? true : this.b() ? false : true; z = () => this.b() ? hello : k; } function foo(arg: T): T extends B ? number : string { if (arg === "A") return 111; return "returning a string"; } +let x = (foo === null || foo === undefined) ? + undefined : + foo.bar.baz(); +let y = (foo === null || foo === undefined) ? /* comment */ undefined : + foo.bar.baz(); + +// Operator ?? +let x = foo ?? bar(); + // Types and logical `&&` and `||` operators after `as` expression Date as any || null; //Assertions const assert: (value: unknown) => asserts value = value => {} declare function assertIsString(value: unknown): asserts value is string; declare function assertIsArrayOfStrings(value: unknown): asserts value is string[]; declare function assertDefined(value: T): asserts value is NonNullable; namespace Debug { export declare function assert(value: unknown, message?: string): asserts value; export declare function assertDefined(value: T): asserts value is NonNullable; } // Only highlight valid regular expressions, of a single line, after strings // See: https://github.com/microsoft/TypeScript-TmLanguage/issues/786 "text" /No-Regex "text" /Regex[:)]*/; const a = "6" / 2; /*comment*/ const b = 5; console.log("4" / "2"); // 2 // Single quote const a = '6' / 2; /*comment*/ const b = 5; console.log('4' / '2'); // 2 // Template const a = `6` / 2; /*comment*/ const b = 5; console.log(`4` / `2`); // 2 + + [1, 2, 3].reduce((previous) => previous, []); + +// Multiline arrow assignment +const h = { + setSubProperty: < + T extends A, + K extends keyof T, + J extends keyof T[K] + >( + property: K, + key: J, + value: T[K][J] + ) => {} +} + +// Private-Named Instance Fields +class Greeter { + #name: string; + constructor(name: string) { + this.#name = name; + } + greet() { + console.log(`hello ${this.#name}`); + } +} +class Test { + #NAME1: () => string; + prop1 = x.#NAMEabc.#NAMEabc; + prop2 = x.#NAMEabc.#NAMEabc(); + prop3 = this.#NAMEabc.#NAME(); +} + +// Type-only imports and exports +import type T from "mod"; +import type { U, V } from "mod"; +import type * as types from "mod"; +import type from "mod"; +import type = require("mod"); +import type T = require("mod"); + +export { a as b, x }; +export * from "AnotherModule"; +export { k as m, l } from "AnotherModule"; +export type { T }; +export type { U } from "AnotherModule"; diff --git a/autotests/input/test.tsx b/autotests/input/test.tsx index 7484dcb..354f0bb 100644 --- a/autotests/input/test.tsx +++ b/autotests/input/test.tsx @@ -1,153 +1,158 @@ // TypeScript React /** @author Willy * @url https://reactjs.org/ **/ import React from 'react'; import { PhotoStory, VideoStory } from './stories'; function Story(props) { const SpecificStory = components[props.storyType]; return ; } function function return class var 0x123 { } &noRef; hello() React.Component() } attr2="&ref;"> /* no comment*/ function return class var 0x123 &ref; hello() React.Component() . anyWord { function return class var 0x123 hello() React.Component() } // Detect Valid tags /* comment */ { /* comment */ word . } return /* comment multiline */ /* comment */ && /*comment*/ & /*comment*/ { Hello } ?; [ ( , = && || return ; default ; return anyWord anyWord/*comment*/ . & | % /* comment*/ // TODO: Fix this (comment before the tag name): var x = ; // Tag after ":" annotation: annotation: text [ ] console.log("hello") // Type assertion in tag /> > // Non-ASCII tag name & attribute <日本語>; ; ; let k1 = hi hi hi! ; let k2 =
My Div
{(name: string) =>
My name {name}
}
; let k3 = ({ x: a.x })} />; // No Error // OK let k1 = <>; // Empty tags hello<> hello <>; // no whitespace < >; // lots of whitespace < /*starting wrap*/ >; // comments in the tags <>hi; // text inside <>hi
bye
; // children <>1<>2.12.23; // nested fragments <>#; // # would cause scanning error if not in jsxtext // Tags after substitutions in templates `aaa${//comment /*comment*/}` // Don't highlight tags within type declaration type T12 = ReturnType<(() => T)>; type T13 = ReturnType<(() => T)>; type T14 = ReturnType; type T15 = ReturnType<(s: string) => void>; // Don't highlight tags within variable declaration let myIdentity: (arg: T) => T = ; var myIdentity: (arg: U) => U = identity; const myIdentity: {(arg: T): T} = identity; // Don't highlight tags within interfaces and classes interface GenericIdentityFn { (arg: T): T; } class Handler { info: (arg: T): T ; } // Highlight "( arg: T ) => { const a = arg return a } text // Here "" is a tag // Check character after tag name, do not highlight invalid tags ; +let x = (foo === null || foo === undefined) ? + undefined :hello; diff --git a/autotests/reference/highlight.php.ref b/autotests/reference/highlight.php.ref index 7d916c0..a7fd325 100644 --- a/autotests/reference/highlight.php.ref +++ b/autotests/reference/highlight.php.ref @@ -1,69 +1,69 @@
/* This is a pseudo PHP file to test Kate's PHP syntax highlighting. */
# TODO: this is incomplete, add more syntax examples!
# this is also a comment.
// Even this is a comment
function test($varname) {
return "bla"; # this is also a comment
}

?>

echo("hello test"); ?>


print "test"; ?>







type="text/babel">
echo("Hello, hello!"); ?> /* aaa */ ?>

function a(i) {
echo "var j = 1;"; ?>
return

{ i + j }

;
}


type="text/typescript">
$timestamp = time(); ?>
class DateTime {
info: string;
- constructor() { this.info = echo(date("F d, Y h:i:s", $timestamp)); ?>; }
- get() { return this.info; }
+ constructor() { this.info = echo(date("F d, Y h:i:s", $timestamp)); ?>; }
+ get() { return this.info; }
}


type="x-tmpl-mustache">
{{! print "comment"; ?> }}
{{#movie}}

{{title}}


src="{{poster}}" alt="{{title}}"/>
echo $movierating; ?> - {{ratings.critics_rating}}

{{/movie}}





$var = <<
This is the $string inside the variable (which seems to be rendered as a string)
It works well, I think.
DOOH

# bug 382527
throw new ParserException("Test {$this->some_var[$index]}\nin {$this->file} is missing.\nThis is bad.");
?>
diff --git a/autotests/reference/test.htm.ref b/autotests/reference/test.htm.ref index 641045f..fe7b753 100644 --- a/autotests/reference/test.htm.ref +++ b/autotests/reference/test.htm.ref @@ -1,74 +1,74 @@ HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">


</Element><Normal Text>This is a title</Normal Text><Element>


class="ui main">
*ngFor="let name of names">Hello, {{ name }}!








type="text/typescript">
class Student {
fullName: string;
- constructor(public firstName: string, public middleInitial: string, public lastName: string) {
+ constructor(public firstName: string, public middleInitial: string, public lastName: string) {
this.fullName = firstName + " " + middleInitial + " " + lastName;
}
}
let a: null = null;
let b: number = 12___3;



src="https://unpkg.com/babel-standalone@6/babel.min.js">
type="text/babel">
ReactDOM.render(

Hello, world!

,
document.getElementById('root')
);
function Story(props) {
const SpecificStory = components[props.storyType];
return story={ props.story } attr2="&ref;" attr3="Hello\n" />;
}



type="x-tmpl-mustache">
{{#movie}}

{{title}}


src="{{poster}}" alt="{{title}}"/>


Rating - {{ratings.critics_rating}}

{{/movie}}
{{^movie}}

Movie Does Not Exist :(

{{/movie}}
{{! comment }}



type="text/html">







diff --git a/autotests/reference/test.markdown.ref b/autotests/reference/test.markdown.ref index 07ca4fd..d14e419 100644 --- a/autotests/reference/test.markdown.ref +++ b/autotests/reference/test.markdown.ref @@ -1,582 +1,582 @@ ---
Title: A Sample Markdown Document
Author: Fletcher T. Penney
Date: February 9, 2011
Comment: This is a comment intended to demonstrate
metadata that spans multiple lines.
---

# H1


## H2


### H3


Multi-line paragraph bla bla bla
bla bla bla.

Intentional line break
via two spaces at line.

Formats: _italic_, **bold**, `monospace`, ~~strikeout~~

Bullet list:

* item1
* item2

Numbered list:

1. item 1
2. item 2

Task list:

- [x] Finish my changes
- [ ] Push my commits
- [ ] Open a pull request

A link as normal text: http://example.com

[link](http://kde.org)

Auto-link: <http://kate-editor.org>
Mail: mark@kde.org>

This is [an example](http://example.com/ "Title") inline link.

See my [About](/about/) page for details.

This is [an example] [id] reference-style link.

[id]: http://example.com/ "Optional Title Here"
[foo]: http://example.com/ 'Optional Title Here'
[bar]: http://example.com/ (Optional Title Here)
[id2]: <http://example.com/> "Optional Title Here"

![Alt text](/path/to/img.jpg)

![Alt text](/path/to/img.jpg "Optional title")

[![Alt text](https://url/to/img.svg)](https://link.com/example/)

code 1
code 2

normal text

>
block quote _italic_
>
more block quote

normal text

Title: some text

normal text

# Lists


1. item
* This is a list item
with multiple lines.
```
code
```

>
This is a
blockquote

This is a new paragraph, which
is part of the item in the
previous list.

indented code block

Other paragraph.

* subitem
* subitem
with multiple
lines.

New paragraph of
the previous subitem.

code block

-----------------------

Other paragraph of
the subitem.

30. numlist item
31. numlist item

New paragraph of
the previous item.

>
Blockquote

code block

Other paragraph.

# Inline HTML


©
bold
code










a b

  • c

  • d

e


normal text *italic*

# Code


`simple code`

``simple ` code``

```simple `` ` code ```

````simple`code````

normal text
normal text
normal text
normal text

code blocks are only allowed
after an empty line.

* list
list

normal text

1. num-list
num-list

normal text

>
quote
quote

code block

normal


code

normal


code

# Fenced code blocks (bug 356974)


## Bash


```bash
#!/usr/bin/env bash

for f *; do
echo "$f"
done
```

## C++


```cpp
#include

class Q : public QObject {
Q_OBJECT
public:
Q();
}

Q::Q() :QObject(NULL) {
}
```

## C


```c
#include
#include "stdint.h"
#define SOME_VAR 1

static uint64_t intWithSuffix = 42ull + ~1233L;
static int octNum = 07232;
```

## C# ##


```csharp
var arr = new[] { 0, 1, 2, 3, 4 };
var anon = new { Country = "Germany", Inhabitants = 80000000 };
```

## CMake


```cmake
cmake_minimum_required(VERSION 3.10 FATAL_ERROR)

project (hello_world)

set(QT_MIN_VERSION "5.6.0")
```

## CSS


```css
h1.main::hover {
font-size: 100;
color: green;
}
```

## Email


```email
From: Konqi Dragon

To: All

Subject: highlights
```

## Go


```go
package main

import "fmt"

func main() {
fmt.Println("Hola, mundo.")
}
```

## Haskell


```haskell
module Main (main) where

main :: IO ()
main = putStrLn "Hello, World!"
```

## HTML


```html


class="main">Hello world!




```

## Java


```java
package fibsandlies;
import java.util.HashMap;

/**
* This is an example of a Javadoc comment; Javadoc can compile documentation
* from this text. Javadoc comments must immediately precede the class, method, or field being documented.
*/
public class FibCalculator extends Fibonacci implements Calculator {

private static Map<Integer, Integer> memoized = new HashMap<Integer, Integer>();
public static void main(String[] args) {
memoized.put(1, 1);
memoized.put(2, 1);
System.out.println(fibonacci(12));
}
}
```

## JavaScript


```js
function factorial(n) {
if (n === 0) {
return 1; // 0! = 1
}
return n * factorial(n - 1);
}
```

## JavaScript React (JSX)


```jsx
/>
function Story(props) {
const SpecificStory = components[props.storyType];
return story={ props.story } attr2="&ref;" attr3="Hello\n" />;
}
```

## JSON


```json
[{
"hello": "world",
"count": 1,
"bool": true
}]
```

## Matlab


```matlab
% Numbers _____________________________________________
5, 5.5, .1, 0.1, 0.4e5, 1.2e-5, 5i, 5.3i, 6j, .345+3i
5', 5.5', .1', 0.1', 0.4e5', 1.2e-5', 5i', 5.3i', 6j', .345+3i'
```

## Markdown


```markdown
**bold** `code`
>
quote

```

## Mustache/Handlebars


```handlebars
{{#movie}}

{{title}}


src="{{poster}}" alt="{{title}}"/>


Rating - {{ratings.critics_rating}}

{{/movie}}
```

## Perl


```perl
my $var = shift;

$var =~ s/bla/foo/igs;
$var =~ s!bla!foo!igs;
$var =~ s#bla#foo#igs;
```

## PHP


```php
namespace Application\Controller;

use Zend\Mvc\Controller\AbstractActionController;

class IndexController extends AbstractActionController
{
}
```

## Python


```python
def addXToY(x, y):
total = x + y
print total
```

## QML


```qml
Text {
id: hello
width: 100
text: "Hello world!"
}
```

## R


```r
# Declare function “f” with parameters “x”, “y“
# that returns a linear combination of x and y.
f <- function(x, y) {
z <- 3 * x + 4 * y
return(z)
}
```

## Ruby


```ruby
require 'Config'
def CGI::escape(string)
string.gsub(/([^ a-zA-Z0-9_.-]+)/n) do
'%' + $1.unpack('H2' * $1.size).join('%').upcase
end.tr(' ', '+')
end
```

## Rust


```rust
fn main() {
println!("Hello world!");
}
```

## MySQL


```mysql
-- comment
CREATE TABLE stuff COLUMNS(col1 INT, col2 Varchar);
SELECT * FROM stuff WHERE id = 'string';
select * from stuff where id < 0.42;
Select col1, col2 From stuff Where stuff.col1 IS NOT NuLL;
```

## TypeScript


```ts
class Student {
fullName: string;
- constructor(public firstName: string, public middleInitial: string, public lastName: string) {
+ constructor(public firstName: string, public middleInitial: string, public lastName: string) {
this.fullName = firstName + " " + middleInitial + " " + lastName;
}
}
```

## XML


```xml
attribute="3">

  • href="link">Hello

  • ```

    ## reStructuredText


    ```rst
    Section Header
    ==============

    .. image:: /path/to/image.jpg

    - A bullet list item
    ```

    ## Doxygen


    ```doxygen
    a normal member taking two arguments and returning an integer value.
    @param a an integer argument.
    @param s a constant character pointer.
    @see Javadoc_Test()
    @see publicVar()
    @return The test results
    ```

    ## No language specified


    ```
    No language is specified, but it should be still rendered as code block.
    ```

    # Fenced code blocks with more than 3 backticks or tildes


    ~~~
    Fenced code block with 3 tildes.
    ~~~

    `````````
    Some implementations of Markdown/MultiMarkdown support more than 3 backticks or tildes.
    ```
    The block ends with the same amount of backticks.
    `````````

    ~~~~~~~~~~~~
    Fenced code block with more tildes.
    ~~~~~~~~~~~~

    ## With languages


    ```````html


    class="main">Hello world!




    ```
    ```````

    ~~~bash
    for f *; do
    echo "$f"
    done
    ~~~

    ~~~~~~~~~python
    def addXToY(x, y):
    total = x + y
    print total
    ~~~ # <= doesn't end here
    ~~~~~~~~~

    # Italic and bold text


    **b** *i* __b__ _i_
    ***ib*** ___ib___
    **_ib_** __*ib*__

    * _italic_ **bold** ***ib*** ~~strikeout~~

    >
    _italic_ **bold** ***ib*** ~~strikeout~~

    ## Bold

    **bold*___text** normal
    __bold_***text__ normal

    ## Italic

    *italic _ text* normal
    _italic * text_ normal

    ## Italic-Bold

    ***italic-bold ** italic-bold*** normal
    ***bold-italic text **bold-italic text*** normal
    ***bold*italic*** normal
    **_bold-italic text_** normal


    _ normal text_ * normal text *
    *italic text*normal text*
    *italic text *italic text*
    **bold text**normal text**
    **bold text **bold text**


    aaa**bold text**aaaaa
    aaa__normal text__aaaaa
    пристаням_стремятся_


    \*normal text*
    _normal text\_
    **italic text\**
    ***only bold\***
    **bold\** bold**
    diff --git a/autotests/reference/test.ts.ref b/autotests/reference/test.ts.ref index b5ca412..b559c8a 100644 --- a/autotests/reference/test.ts.ref +++ b/autotests/reference/test.ts.ref @@ -1,225 +1,280 @@ /// types="node" />

    // TypeScript Test

    class Student {
    fullName: string;
    - constructor(public firstName: string, public middleInitial: string, public lastName: string) {
    + constructor(public firstName: string, public middleInitial: string, public lastName: string) {
    this.fullName = firstName + " " + middleInitial + " " + lastName;
    }
    }

    interface Person {
    firstName: string;
    lastName: string;
    }

    -function greeter(person : Person) {
    +function greeter(person : Person) {
    return "Hello, " + person.firstName + " " + person.lastName;
    }

    let user = new Student("Jane", "M.", "User");

    // Built-in
    -document.body.innerHTML = greeter(user); to
    -JSON.stringify()
    +document.body.innerHTML = greeter(user); to
    +JSON.stringify()
    console.log("Hello world");
    const os = require('os');
    Math.LOG10E;
    Number.MAX_SAFE_INTEGER;

    import http = require("http");
    import path = require("path");
    import URL = url.URL;
    import { Readable, Writable } from "stream";
    import { isBuffer, isString, isObject } from "util";

    const port = 8__88___8;
    const baseUrl = new URL(${port});
    const rootDir = path.dirname(__dirname);

    -const defaultBrowser = os.platform() === "win32" ? "edge" : "chrome";
    +const defaultBrowser = os.platform() === "win32" ? "edge" : "chrome";
    let browser: "edge" | "chrome" | "none" = defaultBrowser;
    let grep: string | undefined;

    interface FileBasedTestConfiguration {
    [setting: string]: string;
    }

    function swapCase(s: string): string {
    - return s.replace(/\w/g, (ch) => {
    - const up = ch.toUpperCase();
    - return ch === up ? ch.toLowerCase() : up;
    - });
    + return s.replace(/\w/g, (ch) => {
    + const up = ch.toUpperCase();
    + return ch === up ? ch.toLowerCase() : up;
    + });
    }

    -for (var i in pieces) {
    - switch (true) {
    - case /^\"?Accession\"?/.test(pieces[i]):
    +for (var i in pieces) {
    + switch (true) {
    + case /^\"?Accession\"?/.test(pieces[i]):
    numeration[0] = i;
    break;
    }
    }

    const enum ResponseCode {
    Success = 200,
    BadRequest = 400
    }

    // Substitutions
    -export function isInOpenInterval(start: number, end: number) {
    - return tersible(a => a > start && a < end, () => ${start}${end})
    +export function isInOpenInterval(start: number, end: number) {
    + return tersible(a => a > start && a < end, () => ${start}${end})
    }
    const bar = ${x}${y};

    // readonly
    -function f1(mt: [number, number], rt: readonly [number, number]) {
    +function f1(mt: [number, number], rt: readonly [number, number]) {
    }

    -function f2(ma: string[], ra: readonly string[], mt: [string, string], rt: readonly [string, string]) {
    +function f2(ma: string[], ra: readonly string[], mt: [string, string], rt: readonly [string, string]) {
    }

    type ReadWrite<T> = { -readonly [P in keyof T] : T[P] };

    // const assertion
    let obj = { x: 10, y: [20, 30], z: { a: { b: 42 } } } as const;
    let r2 = { x: 20, y: 10 } as const;
    let r2 = {...p} as const;
    let p1 = { x: 10, y: 20 as const };
    let p2 = { 10 as const, 'foo' as const };

    // Definite assignment operator
    class Cl {
    one?: string;
    two!: string;
    }
    let x! = 1;

    // Function with multiple arguments
    const children = [].map<Set<Map<number, string>>>(element => {
    - if (!this.identityProvider) {
    + if (!this.identityProvider) {
    return element;
    }
    return element;
    -});
    +});

    // Numerics
    var a = 0xA;
    var b = 0b1;
    var c = 0o7;
    var d = 1.1E+3;
    var e = 1.E+3;
    var f = .1E+3;
    var g = 1E+3;
    var h = 1.1;
    var i = 1.;
    var j = .1;
    var k = 1;
    var l = 1__.e+3_22 | .2____e2 | 0o1_23 | 11__. ;

    // Bigint
    const binBig = 0b101n;
    const octBig = 0o567n;
    const hexBig = 0xC0Bn;
    const decBig = 123n;

    // Types
    let a: null = null;
    let b: number = 123;
    let c: number = 123.456;
    let d: string = ;
    let e: undefined = undefined;
    let f: boolean = true;
    let g: number = 0b111001; // Binary
    let h: number = 0o436; // Octal
    let i: number = 0xadf0d; // Hexa-Decimal
    let j: bigint = 123n;

    const query = query<[number], number>(


    )

    -function runQuery() {
    +function runQuery() {
    const query = createQuery<[number[]], Table<Columns>>(

    )
    - return database.execute(query)
    + return database.execute(query)
    }

    aa: <sdf/> string ?<ssd/> string
    string // Don't highlight
    aa: string assa |
    string
    string ;
    string

    aa: { string
    string } // Don't highlight

    aa: [ string
    string ]
    aa: ( string
    - string ) // Don't highlight
    + string ) // Don't highlight
    aa: string <string>
    interface a {
    aa: /* comment
    */ string,
    bb: /* comment */
    number,
    cc: // comment
    void,
    dd:
    any,
    }

    null, <{[key]: () => any}> null
    null, <{[key]: () =>{a: number}}> null

    // Correctly highlighting regular expressions
    -dt = ((dt[0] * 1e9 + dt[1]) / 1e6).toFixed(3).replace(/\.?0+$/, '') + 'ms';
    +dt = ((dt[0] * 1e9 + dt[1]) / 1e6).toFixed(3).replace(/\.?0+$/, '') + 'ms';
    (a[0] / 2) / (2)

    // Type guards
    function isNumber(x: any): x is number {
    return typeof x === "number";
    }

    // Conditional expressions (highlight keywords before ":")
    class C {
    - w = () =>
    - this.a() ? true : this.b() ? false : true;
    - z = () =>
    - this.b() ? hello : k;
    + w = () =>
    + this.a() ? true : this.b() ? false : true;
    + z = () =>
    + this.b() ? hello : k;
    }

    -function foo<T extends X>(arg: T): T extends B ? number : string {
    +function foo<T extends X>(arg: T): T extends B ? number : string {
    if (arg === "A") return <T extends B ? number : never>111;
    return <T extends B ? never : string>"returning a string";
    }

    +let x = (foo === null || foo === undefined) ?
    + undefined :
    + foo.bar.baz();
    +let y = (foo === null || foo === undefined) ? /* comment */ undefined :
    + foo.bar.baz();
    +
    +// Operator ??
    +let x = foo ?? bar();
    +
    // Types and logical `&&` and `||` operators after `as` expression
    Date as any || null;

    //Assertions
    const assert: (value: unknown) => asserts value = value => {}
    -declare function assertIsString(value: unknown): asserts value is string;
    -declare function assertIsArrayOfStrings(value: unknown): asserts value is string[];
    -declare function assertDefined<T>(value: T): asserts value is NonNullable<T>;
    +declare function assertIsString(value: unknown): asserts value is string;
    +declare function assertIsArrayOfStrings(value: unknown): asserts value is string[];
    +declare function assertDefined<T>(value: T): asserts value is NonNullable<T>;
    namespace Debug {
    - export declare function assert(value: unknown, message?: string): asserts value;
    - export declare function assertDefined<T>(value: T): asserts value is NonNullable<T>;
    + export declare function assert(value: unknown, message?: string): asserts value;
    + export declare function assertDefined<T>(value: T): asserts value is NonNullable<T>;
    }

    // Only highlight valid regular expressions, of a single line, after strings
    // See: https://github.com/microsoft/TypeScript-TmLanguage/issues/786
    "text" /No-Regex
    "text" /Regex[:)]*/;
    const a = "6" / 2; /*comment*/ const b = 5;
    console.log("4" / "2"); // 2
    // Single quote
    const a = '6' / 2; /*comment*/ const b = 5;
    console.log('4' / '2'); // 2
    // Template
    const a = / 2; /*comment*/ const b = 5;
    console.log( / ); // 2
    +
    + [1, 2, 3].reduce<readonly number[]>((previous) => previous, []);
    +
    +// Multiline arrow assignment
    +const h = {
    + setSubProperty: <
    + T extends A,
    + K extends keyof T,
    + J extends keyof T[K]
    + >(
    + property: K,
    + key: J,
    + value: T[K][J]
    + ) => {}
    +}
    +
    +// Private-Named Instance Fields
    +class Greeter {
    + #name: string;
    + constructor(name: string) {
    + this.#name = name;
    + }
    + greet() {
    + console.log(${this.#name});
    + }
    +}
    +class Test {
    + #NAME1: () => string;
    + prop1 = x.#NAMEabc.#NAMEabc;
    + prop2 = x.#NAMEabc.#NAMEabc();
    + prop3 = this.#NAMEabc.#NAME();
    +}
    +
    +// Type-only imports and exports
    +import type T from "mod";
    +import type { U, V } from "mod";
    +import type * as types from "mod";
    +import type from "mod";
    +import type = require("mod");
    +import type T = require("mod");
    +
    +export { a as b, x };
    +export * from "AnotherModule";
    +export { k as m, l } from "AnotherModule";
    +export type { T };
    +export type { U } from "AnotherModule";
    diff --git a/autotests/reference/test.tsx.ref b/autotests/reference/test.tsx.ref index e927345..5dfda21 100644 --- a/autotests/reference/test.tsx.ref +++ b/autotests/reference/test.tsx.ref @@ -1,153 +1,158 @@ // TypeScript React

    /** @author Willy
    * @url https://reactjs.org/ **/

    import React from 'react';
    import { PhotoStory, VideoStory } from './stories';

    -function Story(props) {
    +function Story(props) {
    const SpecificStory = components[props.storyType];
    return story={ props.story } attr2="&ref;" attr3="Hello\n" />;
    }

    function
    - attr1={ function <noTag/> return class var 0x123 { } &noRef; hello() React.Component() } attr2="&ref;">
    + attr1={ function <noTag/> return class var 0x123 { } &noRef; hello() React.Component() } attr2="&ref;">
    /* no comment*/ function return class var 0x123 &ref; hello() React.Component()
    . anyWord
    - { function <tag> return class var 0x123 hello() React.Component() }
    + { function <tag> return class var 0x123 hello() React.Component() }



    Tag$>


    /*comment*/attr1/*comment*/= /*comment*/"value"/*comment*/attr2 /*comment*/attr3='a' key/*comment*/key2 />

    // Detect Valid tags

    /* comment */
    { /* comment
    */ />
    word <noTag/> . <noTag/> } <noTag/>
    return /* comment
    multiline */ /* comment */
    && /*comment*/
    & /*comment*/ <noTag/>


    { Hello }
    ? />;
    [ /> ( />
    , =
    && ||
    return ;
    default ;
    return


    anyWord<noTag>
    anyWord/*comment*/ <noTag/>
    .<noTag>
    &<notag> | <noTag/>
    % /* comment*/ <noTag/>

    // TODO: Fix this (comment before the tag name):
    var x = </**/div>div>;

    // Tag after ":"
    annotation:
    annotation: text [ ]
    console.log("hello")

    // Type assertion in tag
    <number>/>
    <number>>

    />

    // Non-ASCII tag name & attribute
    <日本語>;
    本本:本-本 aa本:本 aa:aa />
    />

    />;
    { ... x } y
    ={2 } z />;

    let k1 =
    a={10} b="hi" {...o} >
    hi hi hi!
    ;

    let k2 =
    a={10} b="hi">
    My Div

    {(name: string) =>
    My name {name}
    }
    ;

    -let k3 = initialValues={{ x: "y" }} nextValues={a => ({ x: a.x })} />; // No Error
    +let k3 = initialValues={{ x: "y" }} nextValues={a => ({ x: a.x })} />; // No Error

    // OK
    let k1 = a={10} b="hi"><> /> />;
    let k2 = a={10} b="hi"><> /> />;
    let k3 = a={10} b="hi"><> /> />;
    let k4 = a={10} b="hi"><> /> />;
    // OK
    let k1 =

    Hello

    world

    ;
    let k2 =

    Hello

    {(user: any) =>

    {user.name}

    }
    ;
    let k3 =
    {1} {"That is a number"}
    ;
    let k4 = ;

    // Empty tags
    hello<>
    hello<string>

    <>; // no whitespace
    < >; // lots of whitespace
    < /*starting wrap*/ > /*ending wrap*/>; // comments in the tags
    <>hi; // text inside
    <>hi
    bye
    ; // children
    <>1<>2.12.23; // nested fragments
    <>#; // # would cause scanning error if not in jsxtext

    // Tags after substitutions in templates
    ${//comment
    /*comment*/}

    // Don't highlight tags within type declaration
    type T12 = ReturnType<(<T>() => T)>;
    type T13 = ReturnType<(<T extends U, U extends number[]>() => T)>;
    type T14 = ReturnType<typeof f1>;
    type T15 = ReturnType<(s: string) => void>;

    // Don't highlight tags within variable declaration
    let myIdentity: <T>(arg: T) => T <noTag/> = />;
    var myIdentity: <U>(arg: U) => U = identity;
    const myIdentity: {<T>(arg: T): T} = identity;

    // Don't highlight tags within interfaces and classes
    interface GenericIdentityFn {
    - <T>(arg: T): T;
    + <T>(arg: T): T;
    <noTag />
    }
    class Handler {
    info: <T>(arg: T): T <noTag />;

    }

    // Highlight "
    // (the "cast" expression works and isn't supported in the TSX file)
    const goodHighlighting = <T extends I>(
    arg: T
    ) => {
    const a = arg
    return a
    }
    text extends I/>
    // Here "" is a tag

    // Check character after tag name, do not highlight invalid tags
    <noTag ?
    <noTag ,
    <noTag /* comment */ ?
    <noTag#
    <noTag/*comment*/#
    +
    +// Conditionals expressions
    +let y = foo == null ? null : ;
    +let x = (foo === null || foo === undefined) ?
    + undefined :hello
    ;
    diff --git a/data/syntax/typescript-react.xml b/data/syntax/typescript-react.xml index c10da67..3f09022 100644 --- a/data/syntax/typescript-react.xml +++ b/data/syntax/typescript-react.xml @@ -1,528 +1,575 @@ + + + ]> - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + - + + + + + + + + + + + + + + diff --git a/data/syntax/typescript.xml b/data/syntax/typescript.xml index a037d47..1601ab7 100644 --- a/data/syntax/typescript.xml +++ b/data/syntax/typescript.xml @@ -1,708 +1,745 @@ - - - - - + + + + + + + + + + ]> controlflow##JavaScript goto keywords##JavaScript - declare reserved##JavaScript type arguments + declare readonly abstract constructor get set module##JavaScript primitives##JavaScript object namespace module keyof infer string number bigint boolean symbol any void never unknown undefined null object class_builtin##JavaScript class_dom##JavaScript class_node##JavaScript variable_property##JavaScript functions##JavaScript console_functions##JavaScript process_property##JavaScript process_functions##JavaScript number_constant##JavaScript math_constant##JavaScript math_functions##JavaScript json_functions##JavaScript promise_functions##JavaScript node_variable##JavaScript dom_variable##JavaScript dom_constant##JavaScript dom_property##JavaScript dom_functions##JavaScript eventhandler_property##JavaScript function_common##JavaScript + + + + + + + + + - - - - + + + - + - - + + + + + + + - + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - + + - + + + + +