Przeglądaj źródła

Fix bad formatting on several operators (#605)

Many, many formatter and syntax highlighting improvements.
David Kincaid 1 rok temu
rodzic
commit
c07fe37f30

+ 18 - 0
package.json

@@ -271,6 +271,24 @@
 					"default": true,
 					"description": "Whether to reveal the terminal when launching the Godot Editor"
 				},
+				"godotTools.formatter.emptyLinesBeforeFunctions": {
+					"type": "string",
+					"enum": [
+						"one",
+						"two"
+					],
+					"enumDescriptions": [
+						"One line before functions. A more compact style.",
+						"Two lines before functions. Conforms to the official GDScript style guide."
+					],
+					"default": "two",
+					"description": "Number of blank lines to leave before functions."
+				},
+				"godotTools.formatter.denseFunctionDeclarations": {
+					"type": "boolean",
+					"default": false,
+					"description": "Whether extra space should be removed from function declarations"
+				},
 				"godotTools.lsp.serverProtocol": {
 					"type": [
 						"string"

+ 5 - 1
src/extension.ts

@@ -1,4 +1,4 @@
-import * as path from "path";
+import * as path from "node:path";
 import * as vscode from "vscode";
 import { attemptSettingsUpdate, get_extension_uri, clean_godot_path } from "./utils";
 import {
@@ -81,6 +81,10 @@ export function activate(context: vscode.ExtensionContext) {
 
 async function initial_setup() {
 	const projectVersion = await get_project_version();
+	if (projectVersion === undefined) {
+		// TODO: actually handle this?
+		return;
+	}
 	const settingName = `editorPath.godot${projectVersion[0]}`;
 	const result = verify_godot_version(get_configuration(settingName), projectVersion[0]);
 	const godotPath = result.godotPath;

+ 2 - 2
src/formatter/formatter.test.ts

@@ -1,6 +1,6 @@
 import * as vscode from "vscode";
-import * as path from "path";
-import * as fs from "fs";
+import * as path from "node:path";
+import * as fs from "node:fs";
 
 import { format_document } from "./textmate";
 

+ 0 - 0
src/formatter/snapshot_template/in.gd


+ 0 - 0
src/formatter/snapshot_template/out.gd


+ 16 - 0
src/formatter/snapshots/arithmetic/in.gd

@@ -26,6 +26,9 @@ func f():
 	a= 1.0* .2
 	a =1.0 * 2.
 
+	a = 10**10
+	a = min(10, 10**10)
+
 	a= a%  b
 	a =1%2
 
@@ -36,3 +39,16 @@ func f():
 
 	var v = Vector2(  1  ,  -   1  )
 	var w = Vector2(1,10-1)
+
+	print( - 1  )
+	print( 1 - 1 )
+
+	print(  - 1 + (1-1))
+	print(  - 1 + (-1-1))
+
+	if a > - 1:
+		if a < - 1:
+			if  a  == -  1:
+				pass
+
+	return  -  1

+ 16 - 0
src/formatter/snapshots/arithmetic/out.gd

@@ -26,6 +26,9 @@ func f():
 	a = 1.0 * .2
 	a = 1.0 * 2.
 
+	a = 10 ** 10
+	a = min(10, 10 ** 10)
+
 	a = a % b
 	a = 1 % 2
 
@@ -36,3 +39,16 @@ func f():
 
 	var v = Vector2(1, -1)
 	var w = Vector2(1, 10 - 1)
+
+	print(-1)
+	print(1 - 1)
+
+	print(-1 + (1 - 1))
+	print(-1 + (-1 - 1))
+
+	if a > -1:
+		if a < -1:
+			if a == -1:
+				pass
+
+	return -1

+ 15 - 0
src/formatter/snapshots/assignment-operators/in.gd

@@ -0,0 +1,15 @@
+func f():
+	# arithmetic
+    x += 1
+	x -= 1
+	x *= 1
+	x /= 1
+	x %= 1
+	
+    # bitwise
+    x |= 1
+	x &= 1
+	x ~= 1
+	x /= 1
+	x >>= 1
+	x <<= 1

+ 15 - 0
src/formatter/snapshots/assignment-operators/out.gd

@@ -0,0 +1,15 @@
+func f():
+	# arithmetic
+    x += 1
+	x -= 1
+	x *= 1
+	x /= 1
+	x %= 1
+	
+    # bitwise
+    x |= 1
+	x &= 1
+	x ~= 1
+	x /= 1
+	x >>= 1
+	x <<= 1

+ 5 - 0
src/formatter/snapshots/bitwise-operators/in.gd

@@ -0,0 +1,5 @@
+func f():
+	collision_mask = 1 << 1 | 1 << 3
+	collision_mask = 1 << 1 & 1 << 3
+	collision_mask = ~1
+	collision_mask = 1  ^  ~    1

+ 5 - 0
src/formatter/snapshots/bitwise-operators/out.gd

@@ -0,0 +1,5 @@
+func f():
+	collision_mask = 1 << 1 | 1 << 3
+	collision_mask = 1 << 1 & 1 << 3
+	collision_mask = ~1
+	collision_mask = 1 ^ ~1

+ 4 - 0
src/formatter/snapshots/boolean-operators/in.gd

@@ -8,3 +8,7 @@ func f():
 func g():
 	print(true and (  not false ) or (  true))
 	print(true and not false or not (true)  )
+
+func h():
+	print(true && (  not false ) || (  true))
+	print(true && not false || not (true)  )

+ 4 - 0
src/formatter/snapshots/boolean-operators/out.gd

@@ -8,3 +8,7 @@ func f():
 func g():
 	print(true and (not false) or (true))
 	print(true and not false or not (true))
+
+func h():
+	print(true && (not false) || (true))
+	print(true && not false || not (true))

+ 21 - 0
src/formatter/snapshots/consecutive-empty-lines-are-removed/in.gd

@@ -1,6 +1,27 @@
 
 
+
+class Test:
+
+
+
+
+	func _ready():
+	
+	
+		pass
+
+
+
 func test():
 
+
 	pass
 
+
+
+
+# comments
+func with_comments():
+
+	pass

+ 14 - 0
src/formatter/snapshots/consecutive-empty-lines-are-removed/out.gd

@@ -1,3 +1,17 @@
+class Test:
+
+
+	func _ready():
+	
+		pass
+
+
 func test():
 
 	pass
+
+
+# comments
+func with_comments():
+
+	pass

+ 6 - 6
src/formatter/snapshots/initialization/out.gd

@@ -2,14 +2,14 @@ var a = 10
 var b := 10
 var c: int = 10
 
-func f(b:=10):
-	return func(c:=10):
+func f(b := 10):
+	return func(c := 10):
 		pass
 
-func f(b: int=10):
-	return func(c: int=10):
+func f(b: int = 10):
+	return func(c: int = 10):
 		pass
 
-func f(b=10):
-	return func(c=10):
+func f(b = 10):
+	return func(c = 10):
 		pass

+ 10 - 0
src/formatter/snapshots/keywords/in.gd

@@ -0,0 +1,10 @@
+func f():
+	const a = preload("res://a.gd")
+	const b = load("res://b.gd")
+
+	var origin: Vector2 = Vector2.ZERO
+	origin.x = 1
+	var andigin: Vector2 = Vector2.ZERO
+	andigin.x = 1
+
+	print(a)

+ 10 - 0
src/formatter/snapshots/keywords/out.gd

@@ -0,0 +1,10 @@
+func f():
+	const a = preload("res://a.gd")
+	const b = load("res://b.gd")
+
+	var origin: Vector2 = Vector2.ZERO
+	origin.x = 1
+	var andigin: Vector2 = Vector2.ZERO
+	andigin.x = 1
+
+	print(a)

+ 4 - 0
src/formatter/snapshots/match/in.gd

@@ -0,0 +1,4 @@
+func f(x):
+    match x:
+        var   y   when y>20:
+            pass

+ 4 - 0
src/formatter/snapshots/match/out.gd

@@ -0,0 +1,4 @@
+func f(x):
+    match x:
+        var y when y > 20:
+            pass

+ 3 - 0
src/formatter/snapshots/nodepaths/in.gd

@@ -69,3 +69,6 @@ var a = $"%Child/GrandChild".some_method()
 var a = $Child.get_node('GrandChild').some_method()
 var a = $"Child".get_node('GrandChild').some_method()
 var a = $"%Child".get_node('GrandChild').some_method()
+
+func f():
+	$Child.add_child(%Unique)

+ 3 - 0
src/formatter/snapshots/nodepaths/out.gd

@@ -69,3 +69,6 @@ var a = $"%Child/GrandChild".some_method()
 var a = $Child.get_node('GrandChild').some_method()
 var a = $"Child".get_node('GrandChild').some_method()
 var a = $"%Child".get_node('GrandChild').some_method()
+
+func f():
+	$Child.add_child(%Unique)

+ 5 - 0
src/formatter/symbols.ts

@@ -21,6 +21,7 @@ export const keywords = [
 	"master",
 	"mastersync",
 	"match",
+	"when",
 	"not",
 	"onready",
 	"or",
@@ -58,10 +59,14 @@ export const symbols = [
 	"&=",
 	"^=",
 	"|=",
+	"~=",
 	"<<=",
 	">>=",
 	":=",
 	"->",
+	"&",
+	"|",
+	"^",
 	"-",
 	"+",
 	"/",

+ 113 - 35
src/formatter/textmate.ts

@@ -1,16 +1,16 @@
-import { Range, TextDocument, TextEdit } from "vscode";
-import * as fs from "fs";
+import { Range, type TextDocument, TextEdit, TextLine } from "vscode";
+import * as fs from "node:fs";
 import * as vsctm from "vscode-textmate";
 import * as oniguruma from "vscode-oniguruma";
 import { keywords, symbols } from "./symbols";
-import { get_extension_uri, createLogger } from "../utils";
+import { get_configuration, get_extension_uri, createLogger } from "../utils";
 
 const log = createLogger("formatter.tm");
 
 // Promisify readFile
 function readFile(path) {
 	return new Promise((resolve, reject) => {
-		fs.readFile(path, (error, data) => error ? reject(error) : resolve(data));
+		fs.readFile(path, (error, data) => (error ? reject(error) : resolve(data)));
 	});
 }
 
@@ -22,17 +22,21 @@ const wasmBin = fs.readFileSync(wasmPath).buffer;
 const registry = new vsctm.Registry({
 	onigLib: oniguruma.loadWASM(wasmBin).then(() => {
 		return {
-			createOnigScanner(patterns) { return new oniguruma.OnigScanner(patterns); },
-			createOnigString(s) { return new oniguruma.OnigString(s); }
+			createOnigScanner(patterns) {
+				return new oniguruma.OnigScanner(patterns);
+			},
+			createOnigString(s) {
+				return new oniguruma.OnigString(s);
+			},
 		};
 	}),
 	loadGrammar: (scopeName) => {
 		if (scopeName === "source.gdscript") {
-			return readFile(grammarPath).then(data => vsctm.parseRawGrammar(data.toString(), grammarPath));
+			return readFile(grammarPath).then((data) => vsctm.parseRawGrammar(data.toString(), grammarPath));
 		}
 		// console.log(`Unknown scope name: ${scopeName}`);
 		return null;
-	}
+	},
 });
 
 interface Token {
@@ -47,6 +51,16 @@ interface Token {
 	skip?: boolean;
 }
 
+class FormatterOptions {
+	emptyLinesBeforeFunctions: "one" | "two";
+	denseFunctionDeclarations: boolean;
+
+	constructor() {
+		this.emptyLinesBeforeFunctions = get_configuration("formatter.emptyLinesBeforeFunctions");
+		this.denseFunctionDeclarations = get_configuration("formatter.denseFunctionDeclarations");
+	}
+}
+
 function parse_token(token: Token) {
 	if (token.scopes.includes("string.quoted.gdscript")) {
 		token.string = true;
@@ -65,6 +79,10 @@ function parse_token(token: Token) {
 		token.type = "symbol";
 		return;
 	}
+	// "preload" is highlighted as a keyword but it behaves like a function
+	if (token.value === "preload") {
+		return;
+	}
 	if (token.scopes.includes("keyword.language.gdscript")) {
 		token.type = "keyword";
 		return;
@@ -79,7 +97,7 @@ function parse_token(token: Token) {
 	}
 }
 
-function between(tokens: Token[], current: number) {
+function between(tokens: Token[], current: number, options: FormatterOptions) {
 	const nextToken = tokens[current];
 	const prevToken = tokens[current - 1];
 	const next = nextToken.value;
@@ -89,26 +107,44 @@ function between(tokens: Token[], current: number) {
 
 	if (!prev) return "";
 
+	if (next === "##") return " ";
 	if (next === "#") return " ";
 	if (prevToken.skip && nextToken.skip) return "";
 
+	if (prev === "(") return "";
+
 	if (nextToken.param) {
-		if (prev === "-" && tokens[current - 2]?.value === ",") {
-			return "";
+		if (options.denseFunctionDeclarations) {
+			if (prev === "-") {
+				if (tokens[current - 2]?.value === "=") return "";
+				if (["keyword", "symbol"].includes(tokens[current - 2].type)) {
+					return "";
+				}
+				if ([",", "("].includes(tokens[current - 2]?.value)) {
+					return "";
+				}
+			}
+			if (next === "%") return " ";
+			if (prev === "%") return " ";
+			if (next === "=") {
+				if (tokens[current - 2]?.value === ":") return " ";
+				return "";
+			}
+			if (prev === "=") {
+				if (tokens[current - 3]?.value === ":") return " ";
+				return "";
+			}
+			if (prevToken?.type === "symbol") return " ";
+			if (nextToken.type === "symbol") return " ";
+		} else {
+			if (next === ":") {
+				if (tokens[current + 1]?.value === "=") return " ";
+			}
 		}
-		if (next === "%") return " ";
-		if (prev === "%") return " ";
-		if (next === "=") return "";
-		if (prev === "=") return "";
-		if (next === ":=") return "";
-		if (prev === ":=") return "";
-		if (prevToken?.type === "symbol") return " ";
-		if (nextToken.type === "symbol") return " ";
 	}
 
 	if (next === ":") {
 		if (["var", "const"].includes(tokens[current - 2]?.value)) {
-			if (tokens[current + 1]?.value !== "=") return "";
 			if (tokens[current + 1]?.value !== "=") return "";
 			return " ";
 		}
@@ -117,7 +153,10 @@ function between(tokens: Token[], current: number) {
 	if (prev === "@") return "";
 
 	if (prev === "-") {
-		if (tokens[current - 2]?.value === "(") {
+		if (["keyword", "symbol"].includes(tokens[current - 2].type)) {
+			return "";
+		}
+		if ([",", "(", "["].includes(tokens[current - 2]?.value)) {
 			return "";
 		}
 	}
@@ -134,6 +173,7 @@ function between(tokens: Token[], current: number) {
 	if (prev === "[" && nextToken.type === "symbol") return "";
 	if (prev === ":") return " ";
 	if (prev === ";") return " ";
+	if (prev === "##") return " ";
 	if (prev === "#") return " ";
 	if (next === "=") return " ";
 	if (prev === "=") return " ";
@@ -157,7 +197,13 @@ function between(tokens: Token[], current: number) {
 
 let grammar = null;
 
-registry.loadGrammar("source.gdscript").then(g => { grammar = g; });
+registry.loadGrammar("source.gdscript").then((g) => {
+	grammar = g;
+});
+
+function is_comment(line: TextLine): boolean {
+	return line.text[line.firstNonWhitespaceCharacterIndex] === "#";
+}
 
 export function format_document(document: TextDocument): TextEdit[] {
 	// quit early if grammar is not loaded
@@ -166,35 +212,67 @@ export function format_document(document: TextDocument): TextEdit[] {
 	}
 	const edits: TextEdit[] = [];
 
+	const options = new FormatterOptions();
+
 	let lineTokens: vsctm.ITokenizeLineResult = null;
 	let onlyEmptyLinesSoFar = true;
+	let emptyLineCount = 0;
+	let firstEmptyLine = 0;
 	for (let lineNum = 0; lineNum < document.lineCount; lineNum++) {
 		const line = document.lineAt(lineNum);
 
 		// skip empty lines
-		if (line.isEmptyOrWhitespace) {
+		if (line.isEmptyOrWhitespace || is_comment(line)) {
 			// delete empty lines at the beginning of the file
 			if (onlyEmptyLinesSoFar) {
 				edits.push(TextEdit.delete(line.rangeIncludingLineBreak));
 			} else {
-				// Limit the number of consecutive empty lines
-				const maxEmptyLines: number = 1;
-				if (maxEmptyLines === 1) {
-					if (lineNum < document.lineCount - 1 && document.lineAt(lineNum + 1).isEmptyOrWhitespace) {
-						edits.push(TextEdit.delete(line.rangeIncludingLineBreak));
-					}
-				} else if (maxEmptyLines === 2) {
-					if (lineNum < document.lineCount - 2 && document.lineAt(lineNum + 1).isEmptyOrWhitespace && document.lineAt(lineNum + 2).isEmptyOrWhitespace) {
-						edits.push(TextEdit.delete(line.rangeIncludingLineBreak));
-					}
+				if (emptyLineCount === 0) {
+					firstEmptyLine = lineNum;
+				}
+				if (!is_comment(line)) {
+					emptyLineCount++;
+				}
+			}
+
+			// delete empty lines at the end of the file
+			if (lineNum === document.lineCount - 1) {
+				for (let i = lineNum - emptyLineCount + 1; i < document.lineCount; i++) {
+					edits.push(TextEdit.delete(document.lineAt(i).rangeIncludingLineBreak));
 				}
 			}
 			continue;
 		}
 		onlyEmptyLinesSoFar = false;
 
+		// delete consecutive empty lines
+		if (emptyLineCount) {
+			let maxEmptyLines = 1;
+
+			const start = line.text.trimStart();
+			if (options.emptyLinesBeforeFunctions === "two") {
+				if (start.startsWith("func") || start.startsWith("static func")) {
+					maxEmptyLines++;
+				}
+			}
+			if (start.startsWith("class")) {
+				maxEmptyLines++;
+			}
+			let i = 0;
+			let deletedLines = 0;
+			const linesToDelete = emptyLineCount - maxEmptyLines;
+			while (i < lineNum && deletedLines < linesToDelete) {
+				const candidate = document.lineAt(firstEmptyLine + i++);
+				if (candidate.isEmptyOrWhitespace) {
+					edits.push(TextEdit.delete(candidate.rangeIncludingLineBreak));
+					deletedLines++;
+				}
+			}
+			emptyLineCount = 0;
+		}
+
 		// skip comments
-		if (line.text[line.firstNonWhitespaceCharacterIndex] === "#") {
+		if (is_comment(line)) {
 			continue;
 		}
 
@@ -228,7 +306,7 @@ export function format_document(document: TextDocument): TextEdit[] {
 			if (i > 0 && tokens[i - 1].string === true && tokens[i].string === true) {
 				nextLine += tokens[i].original;
 			} else {
-				nextLine += between(tokens, i) + tokens[i].value.trim();
+				nextLine += between(tokens, i, options) + tokens[i].value.trim();
 			}
 		}
 

+ 4 - 4
src/utils/project_utils.ts

@@ -1,8 +1,8 @@
 import * as vscode from "vscode";
-import * as path from "path";
-import * as fs from "fs";
-import * as os from "os";
-import { execSync } from "child_process";
+import * as path from "node:path";
+import * as fs from "node:fs";
+import * as os from "node:os";
+import { execSync } from "node:child_process";
 
 let projectDir: string | undefined = undefined;
 let projectFile: string | undefined = undefined;

+ 55 - 17
syntaxes/GDScript.tmLanguage.json

@@ -55,11 +55,10 @@
 				{ "include": "#nodepath_object" },
 				{ "include": "#nodepath_function" },
 				{ "include": "#strings" },
+				{ "include": "#builtin_classes" },
 				{ "include": "#const_vars" },
 				{ "include": "#keywords" },
-				{ "include": "#logic_operator" },
-				{ "include": "#compare_operator" },
-				{ "include": "#arithmetic_operator" },
+				{ "include": "#operators" },
 				{ "include": "#lambda_declaration" },
 				{ "include": "#class_declaration" },
 				{ "include": "#variable_declaration" },
@@ -77,13 +76,12 @@
 				{ "include": "#func" },
 				{ "include": "#letter" },
 				{ "include": "#numbers" },
-				{ "include": "#builtin_classes" },
 				{ "include": "#pascal_case_class" },
 				{ "include": "#line_continuation" }
 			]
 		},
 		"comment": {
-			"match": "(#).*$\\n?",
+			"match": "(##|#).*$\\n?",
 			"name": "comment.line.number-sign.gdscript",
 			"captures": { "1": { "name": "punctuation.definition.comment.number-sign.gdscript" } }
 		},
@@ -97,14 +95,37 @@
 					"name": "constant.character.escape.gdscript",
 					"match": "\\\\."
 				},
-				{ "include": "#string_formatting" }
+				{ "include": "#string_percent_placeholders" },
+				{ "include": "#string_bracket_placeholders" }
 			]
 		},
-		"string_formatting": {
+		"string_percent_placeholders": {
 			"name": "meta.format.percent.gdscript",
 			"match": "(?x)\n  (\n % (\\([\\w\\s]*\\))?\n [-+#0 ]*\n (\\d+|\\*)? (\\.(\\d+|\\*))?\n ([hlL])?\n [diouxXeEfFgGcrsab%]\n  )\n",
 			"captures": { "1": { "name": "constant.character.format.placeholder.other.gdscript" } }
 		},
+		"string_bracket_placeholders": {
+			"patterns": [
+				{
+					"name": "meta.format.brace.gdscript",
+					"match": "(?x)\n  (\n    {{ | }}\n    | (?:\n      {\n        \\w* (\\.[[:alpha:]_]\\w* | \\[[^\\]'\"]+\\])*\n        (![rsa])?\n        ( : \\w? [<>=^]? [-+ ]? \\#?\n          \\d* ,? (\\.\\d+)? [bcdeEfFgGnosxX%]? )?\n      })\n  )\n",
+					"captures": {
+						"1": { "name": "constant.character.format.placeholder.other.gdscript" },
+						"3": { "name": "storage.type.format.gdscript" },
+						"4": { "name": "storage.type.format.gdscript" }
+					}
+				},
+				{
+					"name": "meta.format.brace.gdscript",
+					"match": "(?x)\n  (\n    {\n      \\w* (\\.[[:alpha:]_]\\w* | \\[[^\\]'\"]+\\])*\n      (![rsa])?\n      (:)\n        [^'\"{}\\n]* (?:\n          \\{ [^'\"}\\n]*? \\} [^'\"{}\\n]*\n        )*\n    }\n  )\n",
+					"captures": {
+						"1": { "name": "constant.character.format.placeholder.other.gdscript" },
+						"3": { "name": "storage.type.format.gdscript" },
+						"4": { "name": "storage.type.format.gdscript" }
+					}
+				}
+			]
+		},
 		"nodepath_object": {
 			"name": "meta.literal.nodepath.gdscript",
 			"begin": "(NodePath)\\s*(?:\\()",
@@ -154,10 +175,6 @@
 			"match": "\\bfunc\\b",
 			"name": "keyword.language.gdscript"
 		},
-		"logic_operator": {
-			"match": "\\b(and|or|not|!)\\b",
-			"name": "keyword.operator.wordlike.gdscript"
-		},
 		"in_keyword": {
 			"patterns": [
 				{
@@ -180,12 +197,33 @@
 				}
 			]
 		},
+		"operators": {
+			"patterns": [
+				{ "include": "#wordlike_operator" },
+				{ "include": "#boolean_operator" },
+				{ "include": "#arithmetic_operator" },
+				{ "include": "#bitwise_operator" },
+				{ "include": "#compare_operator" }
+			]
+		},
+		"wordlike_operator": {
+			"match": "\\b(and|or|not)\\b",
+			"name": "keyword.operator.wordlike.gdscript"
+		},
+		"boolean_operator": {
+			"match": "(&&|\\|\\|)",
+			"name": "keyword.operator.boolean.gdscript"
+		},
+		"bitwise_operator": {
+			"match": "&|\\||<<=|>>=|<<|>>|\\^|~",
+			"name": "keyword.operator.bitwise.gdscript"
+		},
 		"compare_operator": {
-			"match": "<=|>=|==|<|>|!=",
+			"match": "<=|>=|==|<|>|!=|!",
 			"name": "keyword.operator.comparison.gdscript"
 		},
 		"arithmetic_operator": {
-			"match": "->|\\+=|-=|\\*=|/=|%=|&=|\\|=|\\*|/|%|\\+|-|<<|>>|&|\\||\\^|~|!",
+			"match": "->|\\+=|-=|\\*=|\\^=|/=|%=|&=|~=|\\|=|\\*\\*|\\*|/|%|\\+|-",
 			"name": "keyword.operator.arithmetic.gdscript"
 		},
 		"assignment_operator": {
@@ -193,11 +231,11 @@
 			"name": "keyword.operator.assignment.gdscript"
 		},
 		"control_flow": {
-			"match": "\\b(?:if|elif|else|while|break|continue|pass|return|match|yield|await)\\b",
+			"match": "\\b(?:if|elif|else|while|break|continue|pass|return|match|when|yield|await)\\b",
 			"name": "keyword.control.gdscript"
 		},
 		"keywords": {
-			"match": "\\b(?:class|class_name|is|onready|tool|static|export|as|void|enum|preload|assert|breakpoint|rpc|sync|remote|master|puppet|slave|remotesync|mastersync|puppetsync|trait|namespace)\\b",
+			"match": "\\b(?:class|class_name|is|onready|tool|static|export|as|void|enum|preload|assert|breakpoint|sync|remote|master|puppet|slave|remotesync|mastersync|puppetsync|trait|namespace)\\b",
 			"name": "keyword.language.gdscript"
 		},
 		"letter": {
@@ -397,7 +435,7 @@
 			"name": "constant.language.gdscript"
 		},
 		"pascal_case_class": {
-			"match": "\\b([A-Z][a-z_0-9]*([A-Z]?[a-z_0-9]+)*[A-Z]?)\\b",
+			"match": "\\b([A-Z]+[a-z_0-9]*([A-Z]?[a-z_0-9]+)*[A-Z]?)\\b",
 			"name": "entity.name.type.class.gdscript"
 		},
 		"signal_declaration_bare": {
@@ -494,7 +532,7 @@
 			"beginCaptures": {
 				"1": { "name": "variable.parameter.function.language.gdscript" },
 				"2": { "name": "punctuation.separator.annotation.gdscript" },
-				"3": { "name": "entity.name.type.class.builtin.gdscript" }
+				"3": { "name": "entity.name.type.class.gdscript" }
 			},
 			"end": "(,)|(?=\\))",
 			"endCaptures": { "1": { "name": "punctuation.separator.parameters.gdscript" } },