Commit 563cd73ea232e2100fea9233956c1564d6dc5214

Michael Schmidt 2021-12-05T14:48:00

Kotlin: Added `char` token and improved string interpolation (#3225)

diff --git a/components/prism-kotlin.js b/components/prism-kotlin.js
index 00dfbe8..3b45dcb 100644
--- a/components/prism-kotlin.js
+++ b/components/prism-kotlin.js
@@ -22,19 +22,60 @@
 
 	delete Prism.languages.kotlin['class-name'];
 
+	var interpolationInside = {
+		'interpolation-punctuation': {
+			pattern: /^\$\{?|\}$/,
+			alias: 'punctuation'
+		},
+		'expression': {
+			pattern: /[\s\S]+/,
+			inside: Prism.languages.kotlin
+		}
+	};
+
 	Prism.languages.insertBefore('kotlin', 'string', {
-		'raw-string': {
-			pattern: /("""|''')[\s\S]*?\1/,
-			alias: 'string'
-			// See interpolation below
+		// https://kotlinlang.org/spec/expressions.html#string-interpolation-expressions
+		'string-literal': [
+			{
+				pattern: /"""(?:[^$]|\$(?:(?!\{)|\{[^{}]*\}))*?"""/,
+				alias: 'multiline',
+				inside: {
+					'interpolation': {
+						pattern: /\$(?:[a-z_]\w*|\{[^{}]*\})/i,
+						inside: interpolationInside
+					},
+					'string': /[\s\S]+/
+				}
+			},
+			{
+				pattern: /"(?:[^"\\\r\n$]|\\.|\$(?:(?!\{)|\{[^{}]*\}))*"/,
+				alias: 'singleline',
+				inside: {
+					'interpolation': {
+						pattern: /((?:^|[^\\])(?:\\{2})*)\$(?:[a-z_]\w*|\{[^{}]*\})/i,
+						lookbehind: true,
+						inside: interpolationInside
+					},
+					'string': /[\s\S]+/
+				}
+			}
+		],
+		'char': {
+			// https://kotlinlang.org/spec/expressions.html#character-literals
+			pattern: /'(?:[^'\\\r\n]|\\(?:.|u[a-fA-F0-9]{0,4}))'/,
+			greedy: true
 		}
 	});
+
+	delete Prism.languages.kotlin['string'];
+
 	Prism.languages.insertBefore('kotlin', 'keyword', {
 		'annotation': {
 			pattern: /\B@(?:\w+:)?(?:[A-Z]\w*|\[[^\]]+\])/,
 			alias: 'builtin'
 		}
 	});
+
 	Prism.languages.insertBefore('kotlin', 'function', {
 		'label': {
 			pattern: /\b\w+@|@\w+\b/,
@@ -42,27 +83,6 @@
 		}
 	});
 
-	var interpolation = [
-		{
-			pattern: /\$\{[^}]+\}/,
-			inside: {
-				'delimiter': {
-					pattern: /^\$\{|\}$/,
-					alias: 'variable'
-				},
-				rest: Prism.languages.kotlin
-			}
-		},
-		{
-			pattern: /\$\w+/,
-			alias: 'variable'
-		}
-	];
-
-	Prism.languages.kotlin['string'].inside = Prism.languages.kotlin['raw-string'].inside = {
-		interpolation: interpolation
-	};
-
 	Prism.languages.kt = Prism.languages.kotlin;
 	Prism.languages.kts = Prism.languages.kotlin;
 }(Prism));
diff --git a/components/prism-kotlin.min.js b/components/prism-kotlin.min.js
index 9cb698a..78a9057 100644
--- a/components/prism-kotlin.min.js
+++ b/components/prism-kotlin.min.js
@@ -1 +1 @@
-!function(e){e.languages.kotlin=e.languages.extend("clike",{keyword:{pattern:/(^|[^.])\b(?:abstract|actual|annotation|as|break|by|catch|class|companion|const|constructor|continue|crossinline|data|do|dynamic|else|enum|expect|external|final|finally|for|fun|get|if|import|in|infix|init|inline|inner|interface|internal|is|lateinit|noinline|null|object|open|operator|out|override|package|private|protected|public|reified|return|sealed|set|super|suspend|tailrec|this|throw|to|try|typealias|val|var|vararg|when|where|while)\b/,lookbehind:!0},function:[{pattern:/(?:`[^\r\n`]+`|\b\w+)(?=\s*\()/,greedy:!0},{pattern:/(\.)(?:`[^\r\n`]+`|\w+)(?=\s*\{)/,lookbehind:!0,greedy:!0}],number:/\b(?:0[xX][\da-fA-F]+(?:_[\da-fA-F]+)*|0[bB][01]+(?:_[01]+)*|\d+(?:_\d+)*(?:\.\d+(?:_\d+)*)?(?:[eE][+-]?\d+(?:_\d+)*)?[fFL]?)\b/,operator:/\+[+=]?|-[-=>]?|==?=?|!(?:!|==?)?|[\/*%<>]=?|[?:]:?|\.\.|&&|\|\||\b(?:and|inv|or|shl|shr|ushr|xor)\b/}),delete e.languages.kotlin["class-name"],e.languages.insertBefore("kotlin","string",{"raw-string":{pattern:/("""|''')[\s\S]*?\1/,alias:"string"}}),e.languages.insertBefore("kotlin","keyword",{annotation:{pattern:/\B@(?:\w+:)?(?:[A-Z]\w*|\[[^\]]+\])/,alias:"builtin"}}),e.languages.insertBefore("kotlin","function",{label:{pattern:/\b\w+@|@\w+\b/,alias:"symbol"}});var n=[{pattern:/\$\{[^}]+\}/,inside:{delimiter:{pattern:/^\$\{|\}$/,alias:"variable"},rest:e.languages.kotlin}},{pattern:/\$\w+/,alias:"variable"}];e.languages.kotlin.string.inside=e.languages.kotlin["raw-string"].inside={interpolation:n},e.languages.kt=e.languages.kotlin,e.languages.kts=e.languages.kotlin}(Prism);
\ No newline at end of file
+!function(n){n.languages.kotlin=n.languages.extend("clike",{keyword:{pattern:/(^|[^.])\b(?:abstract|actual|annotation|as|break|by|catch|class|companion|const|constructor|continue|crossinline|data|do|dynamic|else|enum|expect|external|final|finally|for|fun|get|if|import|in|infix|init|inline|inner|interface|internal|is|lateinit|noinline|null|object|open|operator|out|override|package|private|protected|public|reified|return|sealed|set|super|suspend|tailrec|this|throw|to|try|typealias|val|var|vararg|when|where|while)\b/,lookbehind:!0},function:[{pattern:/(?:`[^\r\n`]+`|\b\w+)(?=\s*\()/,greedy:!0},{pattern:/(\.)(?:`[^\r\n`]+`|\w+)(?=\s*\{)/,lookbehind:!0,greedy:!0}],number:/\b(?:0[xX][\da-fA-F]+(?:_[\da-fA-F]+)*|0[bB][01]+(?:_[01]+)*|\d+(?:_\d+)*(?:\.\d+(?:_\d+)*)?(?:[eE][+-]?\d+(?:_\d+)*)?[fFL]?)\b/,operator:/\+[+=]?|-[-=>]?|==?=?|!(?:!|==?)?|[\/*%<>]=?|[?:]:?|\.\.|&&|\|\||\b(?:and|inv|or|shl|shr|ushr|xor)\b/}),delete n.languages.kotlin["class-name"];var e={"interpolation-punctuation":{pattern:/^\$\{?|\}$/,alias:"punctuation"},expression:{pattern:/[\s\S]+/,inside:n.languages.kotlin}};n.languages.insertBefore("kotlin","string",{"string-literal":[{pattern:/"""(?:[^$]|\$(?:(?!\{)|\{[^{}]*\}))*?"""/,alias:"multiline",inside:{interpolation:{pattern:/\$(?:[a-z_]\w*|\{[^{}]*\})/i,inside:e},string:/[\s\S]+/}},{pattern:/"(?:[^"\\\r\n$]|\\.|\$(?:(?!\{)|\{[^{}]*\}))*"/,alias:"singleline",inside:{interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$(?:[a-z_]\w*|\{[^{}]*\})/i,lookbehind:!0,inside:e},string:/[\s\S]+/}}],char:{pattern:/'(?:[^'\\\r\n]|\\(?:.|u[a-fA-F0-9]{0,4}))'/,greedy:!0}}),delete n.languages.kotlin.string,n.languages.insertBefore("kotlin","keyword",{annotation:{pattern:/\B@(?:\w+:)?(?:[A-Z]\w*|\[[^\]]+\])/,alias:"builtin"}}),n.languages.insertBefore("kotlin","function",{label:{pattern:/\b\w+@|@\w+\b/,alias:"symbol"}}),n.languages.kt=n.languages.kotlin,n.languages.kts=n.languages.kotlin}(Prism);
\ No newline at end of file
diff --git a/tests/languages/kotlin/char_feature.test b/tests/languages/kotlin/char_feature.test
new file mode 100644
index 0000000..8f59a65
--- /dev/null
+++ b/tests/languages/kotlin/char_feature.test
@@ -0,0 +1,11 @@
+'a'
+'\n'
+'\uFFFF'
+
+----------------------------------------------------
+
+[
+	["char", "'a'"],
+	["char", "'\\n'"],
+	["char", "'\\uFFFF'"]
+]
diff --git a/tests/languages/kotlin/interpolation_feature.test b/tests/languages/kotlin/interpolation_feature.test
deleted file mode 100644
index 44ec667..0000000
--- a/tests/languages/kotlin/interpolation_feature.test
+++ /dev/null
@@ -1,46 +0,0 @@
-"$foo ${bar} ${'$'} ${foobar()}"
-"""
-$foo ${bar}
-${'$'} ${foobar()}
-"""
-
-----------------------------------------------------
-
-[
-	["string", [
-		"\"",
-		["interpolation", "$foo"],
-		["interpolation", [
-			["delimiter", "${"], "bar", ["delimiter", "}"]
-		]],
-		["interpolation", [
-			["delimiter", "${"], ["string", ["'$'"]], ["delimiter", "}"]
-		]],
-		["interpolation", [
-			["delimiter", "${"],
-			["function", "foobar"], ["punctuation", "("], ["punctuation", ")"],
-			["delimiter", "}"]
-		]],
-		"\""
-	]],
-	["raw-string", [
-		"\"\"\"\r\n",
-		["interpolation", "$foo"],
-		["interpolation", [
-			["delimiter", "${"], "bar", ["delimiter", "}"]
-		]],
-		["interpolation", [
-			["delimiter", "${"], ["string", ["'$'"]], ["delimiter", "}"]
-		]],
-		["interpolation", [
-			["delimiter", "${"],
-			["function", "foobar"], ["punctuation", "("], ["punctuation", ")"],
-			["delimiter", "}"]
-		]],
-		"\r\n\"\"\""
-	]]
-]
-
-----------------------------------------------------
-
-Checks for string interpolation.
\ No newline at end of file
diff --git a/tests/languages/kotlin/raw-string_feature.test b/tests/languages/kotlin/raw-string_feature.test
deleted file mode 100644
index 5a5ca33..0000000
--- a/tests/languages/kotlin/raw-string_feature.test
+++ /dev/null
@@ -1,18 +0,0 @@
-""""""
-"""Foo "bar"" baz"""
-"""
-"Foo"
-bar
-"""
-
-----------------------------------------------------
-
-[
-	["raw-string", ["\"\"\"\"\"\""]],
-	["raw-string", ["\"\"\"Foo \"bar\"\" baz\"\"\""]],
-	["raw-string", ["\"\"\"\r\n\"Foo\"\r\nbar\r\n\"\"\""]]
-]
-
-----------------------------------------------------
-
-Checks for raw strings.
\ No newline at end of file
diff --git a/tests/languages/kotlin/string_feature.test b/tests/languages/kotlin/string_feature.test
new file mode 100644
index 0000000..921f51e
--- /dev/null
+++ b/tests/languages/kotlin/string_feature.test
@@ -0,0 +1,113 @@
+""
+"foo"
+"\"\""
+
+""""""
+"""Foo "bar"" baz"""
+"""
+"Foo"
+bar
+"""
+
+// interpolation
+
+"$foo ${bar} ${'$'} ${foobar()}"
+"""
+$foo ${bar}
+${'$'} ${foobar()}
+"""
+
+----------------------------------------------------
+
+[
+	["string-literal", [
+		["string", "\"\""]
+	]],
+	["string-literal", [
+		["string", "\"foo\""]
+	]],
+	["string-literal", [
+		["string", "\"\\\"\\\"\""]
+	]],
+
+	["string-literal", [
+		["string", "\"\"\"\"\"\""]
+	]],
+	["string-literal", [
+		["string", "\"\"\"Foo \"bar\"\" baz\"\"\""]
+	]],
+	["string-literal", [
+		["string", "\"\"\"\r\n\"Foo\"\r\nbar\r\n\"\"\""]
+	]],
+
+	["comment", "// interpolation"],
+
+	["string-literal", [
+		["string", "\""],
+		["interpolation", [
+			["interpolation-punctuation", "$"],
+			["expression", ["foo"]]
+		]],
+		["string", " "],
+		["interpolation", [
+			["interpolation-punctuation", "${"],
+			["expression", ["bar"]],
+			["interpolation-punctuation", "}"]
+		]],
+		["string", " "],
+		["interpolation", [
+			["interpolation-punctuation", "${"],
+			["expression", [
+				["char", "'$'"]
+			]],
+			["interpolation-punctuation", "}"]
+		]],
+		["string", " "],
+		["interpolation", [
+			["interpolation-punctuation", "${"],
+			["expression", [
+				["function", "foobar"],
+				["punctuation", "("],
+				["punctuation", ")"]
+			]],
+			["interpolation-punctuation", "}"]
+		]],
+		["string", "\""]
+	]],
+	["string-literal", [
+		["string", "\"\"\"\r\n"],
+		["interpolation", [
+			["interpolation-punctuation", "$"],
+			["expression", ["foo"]]
+		]],
+		["string", " "],
+		["interpolation", [
+			["interpolation-punctuation", "${"],
+			["expression", ["bar"]],
+			["interpolation-punctuation", "}"]
+		]],
+		["string", "\r\n"],
+		["interpolation", [
+			["interpolation-punctuation", "${"],
+			["expression", [
+				["char", "'$'"]
+			]],
+			["interpolation-punctuation", "}"]
+		]],
+		["string", " "],
+		["interpolation", [
+			["interpolation-punctuation", "${"],
+			["expression", [
+				["function", "foobar"],
+				["punctuation", "("],
+				["punctuation", ")"]
+			]],
+			["interpolation-punctuation", "}"]
+		]],
+		["string", "\r\n\"\"\""]
+	]]
+]
+
+----------------------------------------------------
+
+Checks for raw strings.