Commit a197cfcdc76a87e1d462c40e7a314ebd7c97c8dc

Michael Schmidt 2020-04-16T22:30:37

Typescript: Added `asserts` keyword and other improvements (#2280)

diff --git a/components/prism-typescript.js b/components/prism-typescript.js
index 54c45bf..f15345b 100644
--- a/components/prism-typescript.js
+++ b/components/prism-typescript.js
@@ -1,7 +1,42 @@
-Prism.languages.typescript = Prism.languages.extend('javascript', {
-	// From JavaScript Prism keyword list and TypeScript language spec: https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md#221-reserved-words
-	'keyword': /\b(?:abstract|as|async|await|break|case|catch|class|const|constructor|continue|debugger|declare|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|is|keyof|let|module|namespace|new|null|of|package|private|protected|public|readonly|return|require|set|static|super|switch|this|throw|try|type|typeof|undefined|var|void|while|with|yield)\b/,
-	'builtin': /\b(?:string|Function|any|number|boolean|Array|symbol|console|Promise|unknown|never)\b/,
-});
+(function (Prism) {
 
-Prism.languages.ts = Prism.languages.typescript;
+	Prism.languages.typescript = Prism.languages.extend('javascript', {
+		'class-name': {
+			pattern: /(\b(?:class|extends|implements|instanceof|interface|new|type)\s+)(?!keyof\b)[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?:\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>)?/,
+			lookbehind: true,
+			greedy: true,
+			inside: null // see below
+		},
+		// From JavaScript Prism keyword list and TypeScript language spec: https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md#221-reserved-words
+		'keyword': /\b(?:abstract|as|asserts|async|await|break|case|catch|class|const|constructor|continue|debugger|declare|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|is|keyof|let|module|namespace|new|null|of|package|private|protected|public|readonly|return|require|set|static|super|switch|this|throw|try|type|typeof|undefined|var|void|while|with|yield)\b/,
+		'builtin': /\b(?:string|Function|any|number|boolean|Array|symbol|console|Promise|unknown|never)\b/,
+	});
+
+	// doesn't work with TS because TS is too complex
+	delete Prism.languages.typescript['parameter'];
+
+	// a version of typescript specifically for highlighting types
+	var typeInside = Prism.languages.extend('typescript', {});
+	delete typeInside['class-name'];
+
+	Prism.languages.typescript['class-name'].inside = typeInside;
+
+	Prism.languages.insertBefore('typescript', 'function', {
+		'generic-function': {
+			// e.g. foo<T extends "bar" | "baz">( ...
+			pattern: /#?[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>(?=\s*\()/,
+			greedy: true,
+			inside: {
+				'function': /^#?[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*/,
+				'generic': {
+					pattern: /<[\s\S]+/, // everything after the first <
+					alias: 'class-name',
+					inside: typeInside
+				}
+			}
+		}
+	});
+
+	Prism.languages.ts = Prism.languages.typescript;
+
+}(Prism));
diff --git a/components/prism-typescript.min.js b/components/prism-typescript.min.js
index 2390f69..1851214 100644
--- a/components/prism-typescript.min.js
+++ b/components/prism-typescript.min.js
@@ -1 +1 @@
-Prism.languages.typescript=Prism.languages.extend("javascript",{keyword:/\b(?:abstract|as|async|await|break|case|catch|class|const|constructor|continue|debugger|declare|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|is|keyof|let|module|namespace|new|null|of|package|private|protected|public|readonly|return|require|set|static|super|switch|this|throw|try|type|typeof|undefined|var|void|while|with|yield)\b/,builtin:/\b(?:string|Function|any|number|boolean|Array|symbol|console|Promise|unknown|never)\b/}),Prism.languages.ts=Prism.languages.typescript;
\ No newline at end of file
+!function(e){e.languages.typescript=e.languages.extend("javascript",{"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|type)\s+)(?!keyof\b)[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?:\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>)?/,lookbehind:!0,greedy:!0,inside:null},keyword:/\b(?:abstract|as|asserts|async|await|break|case|catch|class|const|constructor|continue|debugger|declare|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|is|keyof|let|module|namespace|new|null|of|package|private|protected|public|readonly|return|require|set|static|super|switch|this|throw|try|type|typeof|undefined|var|void|while|with|yield)\b/,builtin:/\b(?:string|Function|any|number|boolean|Array|symbol|console|Promise|unknown|never)\b/}),delete e.languages.typescript.parameter;var n=e.languages.extend("typescript",{});delete n["class-name"],e.languages.typescript["class-name"].inside=n,e.languages.insertBefore("typescript","function",{"generic-function":{pattern:/#?[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>(?=\s*\()/,greedy:!0,inside:{function:/^#?[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*/,generic:{pattern:/<[\s\S]+/,alias:"class-name",inside:n}}}}),e.languages.ts=e.languages.typescript}(Prism);
\ No newline at end of file
diff --git a/tests/languages/typescript/class-name_feature.test b/tests/languages/typescript/class-name_feature.test
new file mode 100644
index 0000000..0a71261
--- /dev/null
+++ b/tests/languages/typescript/class-name_feature.test
@@ -0,0 +1,125 @@
+interface Dictionary<T> {
+	[key: string]: T;
+}
+
+interface Foo extends Dictionary<number> {}
+
+class Bar<T> extends Baz<T> implements FooBar<number, T | null> {}
+
+type Record<K extends keyof any, T> = {
+	[P in K]: T;
+}
+type Diff<T, U> = T extends U ? never : T;
+
+----------------------------------------------------
+
+[
+	["keyword", "interface"],
+	["class-name", [
+		"Dictionary",
+		["operator", "<"],
+		["constant", "T"],
+		["operator", ">"]
+	]],
+	["punctuation", "{"],
+	["punctuation", "["],
+	"key",
+	["operator", ":"],
+	["builtin", "string"],
+	["punctuation", "]"],
+	["operator", ":"],
+	["constant", "T"],
+	["punctuation", ";"],
+	["punctuation", "}"],
+
+	["keyword", "interface"],
+	["class-name", [
+		"Foo"
+	]],
+	["keyword", "extends"],
+	["class-name", [
+		"Dictionary",
+		["operator", "<"],
+		["builtin", "number"],
+		["operator", ">"]
+	]],
+	["punctuation", "{"],
+	["punctuation", "}"],
+
+	["keyword", "class"],
+	["class-name", [
+		"Bar",
+		["operator", "<"],
+		["constant", "T"],
+		["operator", ">"]
+	]],
+	["keyword", "extends"],
+	["class-name", [
+		"Baz",
+		["operator", "<"],
+		["constant", "T"],
+		["operator", ">"]
+	]],
+	["keyword", "implements"],
+	["class-name", [
+		"FooBar",
+		["operator", "<"],
+		["builtin", "number"],
+		["punctuation", ","],
+		["constant", "T"],
+		["operator", "|"],
+		["keyword", "null"],
+		["operator", ">"]
+	]],
+	["punctuation", "{"],
+	["punctuation", "}"],
+
+	["keyword", "type"],
+	["class-name", [
+		"Record",
+		["operator", "<"],
+		["constant", "K"],
+		["keyword", "extends"],
+		["keyword", "keyof"],
+		["builtin", "any"],
+		["punctuation", ","],
+		["constant", "T"],
+		["operator", ">"]
+	]],
+	["operator", "="],
+	["punctuation", "{"],
+	["punctuation", "["],
+	["constant", "P"],
+	["keyword", "in"],
+	["constant", "K"],
+	["punctuation", "]"],
+	["operator", ":"],
+	["constant", "T"],
+	["punctuation", ";"],
+	["punctuation", "}"],
+
+	["keyword", "type"],
+	["class-name", [
+		"Diff",
+		["operator", "<"],
+		["constant", "T"],
+		["punctuation", ","],
+		["constant", "U"],
+		["operator", ">"]
+	]],
+	["operator", "="],
+	["constant", "T"],
+	["keyword", "extends"],
+	["class-name", [
+		["constant", "U"]
+	]],
+	["operator", "?"],
+	["builtin", "never"],
+	["operator", ":"],
+	["constant", "T"],
+	["punctuation", ";"]
+]
+
+----------------------------------------------------
+
+Checks for class, interface, and other type names.
diff --git a/tests/languages/typescript/function_feature.test b/tests/languages/typescript/function_feature.test
new file mode 100644
index 0000000..3b0fd70
--- /dev/null
+++ b/tests/languages/typescript/function_feature.test
@@ -0,0 +1,91 @@
+function getProperty<T, K extends keyof T>(o: T, propertyName: K): T[K] { }
+
+declare function f<T extends boolean>(x: T): T extends true ? string : number;
+
+function assert(condition: any, msg?: string): asserts condition { }
+
+----------------------------------------------------
+
+[
+	["keyword", "function"],
+	["generic-function", [
+		["function", "getProperty"],
+		["generic", [
+			["operator", "<"],
+			["constant", "T"],
+			["punctuation", ","],
+			["constant", "K"],
+			["keyword", "extends"],
+			["keyword", "keyof"],
+			["constant", "T"],
+			["operator", ">"]
+		]]
+	]],
+	["punctuation", "("],
+	"o",
+	["operator", ":"],
+	["constant", "T"],
+	["punctuation", ","],
+	" propertyName",
+	["operator", ":"],
+	["constant", "K"],
+	["punctuation", ")"],
+	["operator", ":"],
+	["constant", "T"],
+	["punctuation", "["],
+	["constant", "K"],
+	["punctuation", "]"],
+	["punctuation", "{"],
+	["punctuation", "}"],
+
+	["keyword", "declare"],
+	["keyword", "function"],
+	["generic-function", [
+		["function", "f"],
+		["generic", [
+			["operator", "<"],
+			["constant", "T"],
+			["keyword", "extends"],
+			["builtin", "boolean"],
+			["operator", ">"]
+		]]
+	]],
+	["punctuation", "("],
+	"x",
+	["operator", ":"],
+	["constant", "T"],
+	["punctuation", ")"],
+	["operator", ":"],
+	["constant", "T"],
+	["keyword", "extends"],
+	["class-name", [
+		["boolean", "true"]
+	]],
+	["operator", "?"],
+	["builtin", "string"],
+	["operator", ":"],
+	["builtin", "number"],
+	["punctuation", ";"],
+
+	["keyword", "function"],
+	["function", "assert"],
+	["punctuation", "("],
+	"condition",
+	["operator", ":"],
+	["builtin", "any"],
+	["punctuation", ","],
+	" msg",
+	["operator", "?"],
+	["operator", ":"],
+	["builtin", "string"],
+	["punctuation", ")"],
+	["operator", ":"],
+	["keyword", "asserts"],
+	" condition ",
+	["punctuation", "{"],
+	["punctuation", "}"]
+]
+
+----------------------------------------------------
+
+Checks for functions.
diff --git a/tests/languages/typescript/keyword_feature.test b/tests/languages/typescript/keyword_feature.test
index 9e19b36..f877e09 100644
--- a/tests/languages/typescript/keyword_feature.test
+++ b/tests/languages/typescript/keyword_feature.test
@@ -1,5 +1,6 @@
 abstract
 as
+asserts
 async
 await
 break
@@ -51,8 +52,9 @@ switch
 this
 throw
 try
-type
+type;
 typeof
+undefined
 var
 void
 while
@@ -64,6 +66,7 @@ yield
 [
 	["keyword", "abstract"],
 	["keyword", "as"],
+	["keyword", "asserts"],
 	["keyword", "async"],
 	["keyword", "await"],
 	["keyword", "break"],
@@ -115,8 +118,9 @@ yield
 	["keyword", "this"],
 	["keyword", "throw"],
 	["keyword", "try"],
-	["keyword", "type"],
+	["keyword", "type"], ["punctuation", ";"],
 	["keyword", "typeof"],
+	["keyword", "undefined"],
 	["keyword", "var"],
 	["keyword", "void"],
 	["keyword", "while"],
@@ -126,4 +130,4 @@ yield
 
 ----------------------------------------------------
 
-Checks for keywords.
\ No newline at end of file
+Checks for keywords.