From 0a794ebc1b09b334c5f77816bc708d1cc330bf8f Mon Sep 17 00:00:00 2001 From: Sandy Gutierrez Date: Fri, 22 Dec 2023 06:04:02 -0500 Subject: [PATCH] Add snapshot tests to formatter (#545) * Add snapshot tests for formatter * Add test runner to CI --------- Co-authored-by: David Kincaid --- .github/workflows/ci.yml | 33 ++- README.md | 1 + package-lock.json | 95 ++++++++ package.json | 2 + src/formatter/formatter.test.ts | 36 ++- src/formatter/snapshots/arithmetic/in.gd | 35 +++ src/formatter/snapshots/arithmetic/out.gd | 35 +++ src/formatter/snapshots/arrays/in.gd | 10 + src/formatter/snapshots/arrays/out.gd | 10 + src/formatter/snapshots/assert/in.gd | 2 + src/formatter/snapshots/assert/out.gd | 2 + src/formatter/snapshots/await/in.gd | 2 + src/formatter/snapshots/await/out.gd | 2 + .../snapshots/boolean-operators/in.gd | 10 + .../snapshots/boolean-operators/out.gd | 10 + .../in.gd} | 4 +- .../out.gd | 3 + src/formatter/snapshots/enums/in.gd | 7 + src/formatter/snapshots/enums/out.gd | 7 + .../indentation-style-is-preserved/in.gd | 17 ++ .../indentation-style-is-preserved/out.gd | 17 ++ src/formatter/snapshots/initialization/in.gd | 15 ++ src/formatter/snapshots/initialization/out.gd | 15 ++ .../snapshots/lambda-functions/in.gd | 5 + .../snapshots/lambda-functions/out.gd | 5 + src/formatter/snapshots/line-breaks/in.gd | 8 + src/formatter/snapshots/line-breaks/out.gd | 8 + src/formatter/snapshots/nodepaths/in.gd | 68 ++++++ src/formatter/snapshots/nodepaths/out.gd | 68 ++++++ src/formatter/snapshots/reserved-words/in.gd | 7 + src/formatter/snapshots/reserved-words/out.gd | 7 + .../snapshots/return-expression/in.gd | 7 + .../snapshots/return-expression/out.gd | 7 + src/formatter/snapshots/return-type/in.gd | 8 + src/formatter/snapshots/return-type/out.gd | 8 + src/formatter/snapshots/semicolon/in.gd | 3 + src/formatter/snapshots/semicolon/out.gd | 3 + src/formatter/snapshots/strings/in.gd | 5 + src/formatter/snapshots/strings/out.gd | 5 + src/formatter/tests/test1.in.gd | 5 - src/formatter/textmate.ts | 60 +++-- syntaxes/GDScript.tmLanguage.json | 228 +++++++++--------- syntaxes/examples/gdscript2.gd | 9 + 43 files changed, 749 insertions(+), 145 deletions(-) create mode 100644 src/formatter/snapshots/arithmetic/in.gd create mode 100644 src/formatter/snapshots/arithmetic/out.gd create mode 100644 src/formatter/snapshots/arrays/in.gd create mode 100644 src/formatter/snapshots/arrays/out.gd create mode 100644 src/formatter/snapshots/assert/in.gd create mode 100644 src/formatter/snapshots/assert/out.gd create mode 100644 src/formatter/snapshots/await/in.gd create mode 100644 src/formatter/snapshots/await/out.gd create mode 100644 src/formatter/snapshots/boolean-operators/in.gd create mode 100644 src/formatter/snapshots/boolean-operators/out.gd rename src/formatter/{tests/test1.out.gd => snapshots/consecutive-empty-lines-are-removed/in.gd} (60%) create mode 100644 src/formatter/snapshots/consecutive-empty-lines-are-removed/out.gd create mode 100644 src/formatter/snapshots/enums/in.gd create mode 100644 src/formatter/snapshots/enums/out.gd create mode 100644 src/formatter/snapshots/indentation-style-is-preserved/in.gd create mode 100644 src/formatter/snapshots/indentation-style-is-preserved/out.gd create mode 100644 src/formatter/snapshots/initialization/in.gd create mode 100644 src/formatter/snapshots/initialization/out.gd create mode 100644 src/formatter/snapshots/lambda-functions/in.gd create mode 100644 src/formatter/snapshots/lambda-functions/out.gd create mode 100644 src/formatter/snapshots/line-breaks/in.gd create mode 100644 src/formatter/snapshots/line-breaks/out.gd create mode 100644 src/formatter/snapshots/nodepaths/in.gd create mode 100644 src/formatter/snapshots/nodepaths/out.gd create mode 100644 src/formatter/snapshots/reserved-words/in.gd create mode 100644 src/formatter/snapshots/reserved-words/out.gd create mode 100644 src/formatter/snapshots/return-expression/in.gd create mode 100644 src/formatter/snapshots/return-expression/out.gd create mode 100644 src/formatter/snapshots/return-type/in.gd create mode 100644 src/formatter/snapshots/return-type/out.gd create mode 100644 src/formatter/snapshots/semicolon/in.gd create mode 100644 src/formatter/snapshots/semicolon/out.gd create mode 100644 src/formatter/snapshots/strings/in.gd create mode 100644 src/formatter/snapshots/strings/out.gd delete mode 100644 src/formatter/tests/test1.in.gd diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eb6446d..d348b23 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,8 +2,12 @@ name: Continuous integration on: [push, pull_request] jobs: - build: - runs-on: ubuntu-20.04 + test: + name: Test + strategy: + matrix: + os: [macos-latest, ubuntu-latest, windows-latest] + runs-on: ${{ matrix.os }} steps: - name: Checkout uses: actions/checkout@v4 @@ -13,9 +17,32 @@ jobs: with: node-version: 16.x + - name: Install dependencies + run: npm install + + - name: Run headless test + uses: coactions/setup-xvfb@v1 + with: + run: | + npm run compile + npm test + + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Node.js + uses: actions/setup-node@v4.0.0 + with: + node-version: 16.x + + - name: Install dependencies + run: npm install + - name: Lint and build extension run: | - npm install npm run lint npm run package -- --out godot-tools.vsix ls -l godot-tools.vsix diff --git a/README.md b/README.md index 96c8fdb..09ecda9 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ Godot 3.2 or later. - `ctrl+click` on any symbol to jump to its definition or **open its documentation** - `ctrl+click` on `res://resource/path` links - **hover previews on `res://resource/path` links** + - **builtin code formatter** - autocompletions - full typed GDScript support - optional "Smart Mode" to improve productivity with dynamically typed scripts diff --git a/package-lock.json b/package-lock.json index 4ec3888..6a43d42 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,7 @@ "ya-bbcode": "^4.0.0" }, "devDependencies": { + "@types/chai": "^4.3.11", "@types/marked": "^4.0.8", "@types/mocha": "^10.0.6", "@types/node": "^18.15.0", @@ -36,6 +37,7 @@ "@vscode/test-cli": "^0.0.4", "@vscode/test-electron": "^2.3.8", "@vscode/vsce": "^2.21.0", + "chai": "^4.3.10", "esbuild": "^0.17.15", "eslint": "^8.37.0", "mocha": "^10.2.0", @@ -765,6 +767,12 @@ "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", "dev": true }, + "node_modules/@types/chai": { + "version": "4.3.11", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.11.tgz", + "integrity": "sha512-qQR1dr2rGIHYlJulmr8Ioq3De0Le9E4MJ5AiaeAETJJpndT1uUNHsGFK3L/UIu+rbkQSdj8J/w2bCsBZc/Y5fQ==", + "dev": true + }, "node_modules/@types/json-schema": { "version": "7.0.13", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz", @@ -1536,6 +1544,15 @@ "node": ">=8" } }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/await-notify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/await-notify/-/await-notify-1.0.1.tgz", @@ -1723,6 +1740,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/chai": { + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.10.tgz", + "integrity": "sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.0.8" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -1737,6 +1772,18 @@ "node": ">=4" } }, + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, "node_modules/cheerio": { "version": "1.0.0-rc.10", "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.10.tgz", @@ -2020,6 +2067,18 @@ "node": ">=8" } }, + "node_modules/deep-eql": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -2804,6 +2863,15 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/get-intrinsic": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", @@ -3448,6 +3516,15 @@ "node": ">=8" } }, + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.1" + } + }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -4053,6 +4130,15 @@ "node": ">=8" } }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/pause-stream": { "version": "0.0.11", "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", @@ -4866,6 +4952,15 @@ "node": ">= 0.8.0" } }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", diff --git a/package.json b/package.json index 640e41c..3da1afd 100644 --- a/package.json +++ b/package.json @@ -802,6 +802,7 @@ } }, "devDependencies": { + "@types/chai": "^4.3.11", "@types/marked": "^4.0.8", "@types/mocha": "^10.0.6", "@types/node": "^18.15.0", @@ -814,6 +815,7 @@ "@vscode/test-cli": "^0.0.4", "@vscode/test-electron": "^2.3.8", "@vscode/vsce": "^2.21.0", + "chai": "^4.3.10", "esbuild": "^0.17.15", "eslint": "^8.37.0", "mocha": "^10.2.0", diff --git a/src/formatter/formatter.test.ts b/src/formatter/formatter.test.ts index 0561626..d384a27 100644 --- a/src/formatter/formatter.test.ts +++ b/src/formatter/formatter.test.ts @@ -1,17 +1,41 @@ -import * as assert from "assert"; import * as vscode from "vscode"; import * as path from "path"; +import * as fs from "fs"; import { format_document } from "./textmate"; +import * as chai from "chai"; +const expect = chai.expect; + const dots = ["..", "..", ".."]; const basePath = path.join(__filename, ...dots); suite("GDScript Formatter Tests", () => { - test("Test Formatting", async () => { - const uri = vscode.Uri.file(path.join(basePath, "src/formatter/tests/test1.in.gd")); - const document = await vscode.workspace.openTextDocument(uri); - const edits = format_document(document); - assert.strictEqual(4, edits.length); + // Search for all folders in the snapshots folder and run a test for each + // comparing the output of the formatter with the expected output. + // To add a new test, create a new folder in the snapshots folder + // and add two files, `in.gd` and `out.gd` for the input and expected output. + const snapshotsFolderPath = path.join(basePath, "src/formatter/snapshots"); + const testFolders = fs.readdirSync(snapshotsFolderPath); + + testFolders.forEach((testFolder) => { + const testFolderPath = path.join(snapshotsFolderPath, testFolder); + if (fs.statSync(testFolderPath).isDirectory()) { + test(`Snapshot Test: ${testFolder}`, async () => { + const uriIn = vscode.Uri.file(path.join(testFolderPath, "in.gd")); + const uriOut = vscode.Uri.file(path.join(testFolderPath, "out.gd")); + const documentIn = await vscode.workspace.openTextDocument(uriIn); + const documentOut = await vscode.workspace.openTextDocument(uriOut); + const edits = format_document(documentIn); + + // Apply the formatting edits + const workspaceEdit = new vscode.WorkspaceEdit(); + workspaceEdit.set(uriIn, edits); + await vscode.workspace.applyEdit(workspaceEdit); + + // Compare the result with the expected output + expect(documentIn.getText()).to.equal(documentOut.getText()); + }); + } }); }); diff --git a/src/formatter/snapshots/arithmetic/in.gd b/src/formatter/snapshots/arithmetic/in.gd new file mode 100644 index 0000000..4d54a44 --- /dev/null +++ b/src/formatter/snapshots/arithmetic/in.gd @@ -0,0 +1,35 @@ +var a = 1 +var b = 2 + +func f(): + a = a+b + a = .1+ 2 + a= 1. +2 + a =1.0 + .2 + a = 1.0+ 2. + + a =a -b + a = .1- 2 + a= 1.-2 + a = 1.0 - .2 + a =1.0- 2. + + a= a/ b + a = .1 /2 + a =1. /2 + a = 1.0 / .2 + a =1.0/2. + + a = a *b + a=.1* 2 + a = 1. *2 + a= 1.0* .2 + a =1.0 * 2. + + a= a% b + a =1%2 + + a =-1 + a= +1 + + a = ((-1 + 2) * (3-4) / 5 * 6%( -7 + 8-9 - 10)) * (- 11 + 12) / (13*14 % 15 +16) diff --git a/src/formatter/snapshots/arithmetic/out.gd b/src/formatter/snapshots/arithmetic/out.gd new file mode 100644 index 0000000..8eef307 --- /dev/null +++ b/src/formatter/snapshots/arithmetic/out.gd @@ -0,0 +1,35 @@ +var a = 1 +var b = 2 + +func f(): + a = a + b + a = .1 + 2 + a = 1. + 2 + a = 1.0 + .2 + a = 1.0 + 2. + + a = a - b + a = .1 - 2 + a = 1. - 2 + a = 1.0 - .2 + a = 1.0 - 2. + + a = a / b + a = .1 / 2 + a = 1. / 2 + a = 1.0 / .2 + a = 1.0 / 2. + + a = a * b + a = .1 * 2 + a = 1. * 2 + a = 1.0 * .2 + a = 1.0 * 2. + + a = a % b + a = 1 % 2 + + a = -1 + a = +1 + + a = ((-1 + 2) * (3 - 4) / 5 * 6 % (-7 + 8 - 9 - 10)) * (-11 + 12) / (13 * 14 % 15 + 16) diff --git a/src/formatter/snapshots/arrays/in.gd b/src/formatter/snapshots/arrays/in.gd new file mode 100644 index 0000000..2fa0f6b --- /dev/null +++ b/src/formatter/snapshots/arrays/in.gd @@ -0,0 +1,10 @@ +var primes = [2, 3, 5, 7, 11, 13, 17 ] +var primes2 = [ + 2 , + 3 , + 5, + 7 , + 11 , + 13, + 17 + ] diff --git a/src/formatter/snapshots/arrays/out.gd b/src/formatter/snapshots/arrays/out.gd new file mode 100644 index 0000000..af052be --- /dev/null +++ b/src/formatter/snapshots/arrays/out.gd @@ -0,0 +1,10 @@ +var primes = [2, 3, 5, 7, 11, 13, 17] +var primes2 = [ + 2, + 3, + 5, + 7, + 11, + 13, + 17 + ] diff --git a/src/formatter/snapshots/assert/in.gd b/src/formatter/snapshots/assert/in.gd new file mode 100644 index 0000000..87a0065 --- /dev/null +++ b/src/formatter/snapshots/assert/in.gd @@ -0,0 +1,2 @@ +func f(): + assert (1!= 2) diff --git a/src/formatter/snapshots/assert/out.gd b/src/formatter/snapshots/assert/out.gd new file mode 100644 index 0000000..99abee1 --- /dev/null +++ b/src/formatter/snapshots/assert/out.gd @@ -0,0 +1,2 @@ +func f(): + assert(1 != 2) diff --git a/src/formatter/snapshots/await/in.gd b/src/formatter/snapshots/await/in.gd new file mode 100644 index 0000000..4df1d13 --- /dev/null +++ b/src/formatter/snapshots/await/in.gd @@ -0,0 +1,2 @@ +func f(): + await delay(10) diff --git a/src/formatter/snapshots/await/out.gd b/src/formatter/snapshots/await/out.gd new file mode 100644 index 0000000..4df1d13 --- /dev/null +++ b/src/formatter/snapshots/await/out.gd @@ -0,0 +1,2 @@ +func f(): + await delay(10) diff --git a/src/formatter/snapshots/boolean-operators/in.gd b/src/formatter/snapshots/boolean-operators/in.gd new file mode 100644 index 0000000..b4d0826 --- /dev/null +++ b/src/formatter/snapshots/boolean-operators/in.gd @@ -0,0 +1,10 @@ +func f(): + print(not true ) + if ( not true) and\ + (not true ): + pass + print(not true ) + +func g(): + print(true and ( not false ) or ( true)) + print(true and not false or not (true) ) diff --git a/src/formatter/snapshots/boolean-operators/out.gd b/src/formatter/snapshots/boolean-operators/out.gd new file mode 100644 index 0000000..7a313de --- /dev/null +++ b/src/formatter/snapshots/boolean-operators/out.gd @@ -0,0 +1,10 @@ +func f(): + print(not true) + if (not true) and \ + (not true): + pass + print(not true) + +func g(): + print(true and (not false) or (true)) + print(true and not false or not (true)) diff --git a/src/formatter/tests/test1.out.gd b/src/formatter/snapshots/consecutive-empty-lines-are-removed/in.gd similarity index 60% rename from src/formatter/tests/test1.out.gd rename to src/formatter/snapshots/consecutive-empty-lines-are-removed/in.gd index db8d4c9..d8ac24c 100644 --- a/src/formatter/tests/test1.out.gd +++ b/src/formatter/snapshots/consecutive-empty-lines-are-removed/in.gd @@ -1,4 +1,6 @@ -extends Node + func test(): + pass + diff --git a/src/formatter/snapshots/consecutive-empty-lines-are-removed/out.gd b/src/formatter/snapshots/consecutive-empty-lines-are-removed/out.gd new file mode 100644 index 0000000..4307a53 --- /dev/null +++ b/src/formatter/snapshots/consecutive-empty-lines-are-removed/out.gd @@ -0,0 +1,3 @@ +func test(): + + pass diff --git a/src/formatter/snapshots/enums/in.gd b/src/formatter/snapshots/enums/in.gd new file mode 100644 index 0000000..9b0c094 --- /dev/null +++ b/src/formatter/snapshots/enums/in.gd @@ -0,0 +1,7 @@ +enum State { A,B, C } + +enum State2 { + A , + B , + C +} diff --git a/src/formatter/snapshots/enums/out.gd b/src/formatter/snapshots/enums/out.gd new file mode 100644 index 0000000..ae65cba --- /dev/null +++ b/src/formatter/snapshots/enums/out.gd @@ -0,0 +1,7 @@ +enum State {A, B, C} + +enum State2 { + A, + B, + C +} diff --git a/src/formatter/snapshots/indentation-style-is-preserved/in.gd b/src/formatter/snapshots/indentation-style-is-preserved/in.gd new file mode 100644 index 0000000..bfb62fb --- /dev/null +++ b/src/formatter/snapshots/indentation-style-is-preserved/in.gd @@ -0,0 +1,17 @@ +func test(): + if true: + pass + else: + pass + +func test(): + if true: + pass + else: + pass + +func test(): + if true: + pass + else: + pass diff --git a/src/formatter/snapshots/indentation-style-is-preserved/out.gd b/src/formatter/snapshots/indentation-style-is-preserved/out.gd new file mode 100644 index 0000000..bfb62fb --- /dev/null +++ b/src/formatter/snapshots/indentation-style-is-preserved/out.gd @@ -0,0 +1,17 @@ +func test(): + if true: + pass + else: + pass + +func test(): + if true: + pass + else: + pass + +func test(): + if true: + pass + else: + pass diff --git a/src/formatter/snapshots/initialization/in.gd b/src/formatter/snapshots/initialization/in.gd new file mode 100644 index 0000000..7e52d1c --- /dev/null +++ b/src/formatter/snapshots/initialization/in.gd @@ -0,0 +1,15 @@ +var a= 10 +var b :=10 +var c: int = 10 + +func f(b :=10): + return func(c : = 10): + pass + +func f(b : int= 10): + return func(c: int=10 ): + pass + +func f( b = 10 ): + return func( c = 10 ): + pass diff --git a/src/formatter/snapshots/initialization/out.gd b/src/formatter/snapshots/initialization/out.gd new file mode 100644 index 0000000..16bc37e --- /dev/null +++ b/src/formatter/snapshots/initialization/out.gd @@ -0,0 +1,15 @@ +var a = 10 +var b := 10 +var c: int = 10 + +func f(b:=10): + return func(c:=10): + pass + +func f(b: int=10): + return func(c: int=10): + pass + +func f(b=10): + return func(c=10): + pass diff --git a/src/formatter/snapshots/lambda-functions/in.gd b/src/formatter/snapshots/lambda-functions/in.gd new file mode 100644 index 0000000..5274e67 --- /dev/null +++ b/src/formatter/snapshots/lambda-functions/in.gd @@ -0,0 +1,5 @@ +func f(): + g(func(): return true + h(func(): + return false + ) diff --git a/src/formatter/snapshots/lambda-functions/out.gd b/src/formatter/snapshots/lambda-functions/out.gd new file mode 100644 index 0000000..5274e67 --- /dev/null +++ b/src/formatter/snapshots/lambda-functions/out.gd @@ -0,0 +1,5 @@ +func f(): + g(func(): return true + h(func(): + return false + ) diff --git a/src/formatter/snapshots/line-breaks/in.gd b/src/formatter/snapshots/line-breaks/in.gd new file mode 100644 index 0000000..69df323 --- /dev/null +++ b/src/formatter/snapshots/line-breaks/in.gd @@ -0,0 +1,8 @@ +func f(): + if a or b\ + or c: + pass + + if a or \ + b or c: + pass diff --git a/src/formatter/snapshots/line-breaks/out.gd b/src/formatter/snapshots/line-breaks/out.gd new file mode 100644 index 0000000..ffba82e --- /dev/null +++ b/src/formatter/snapshots/line-breaks/out.gd @@ -0,0 +1,8 @@ +func f(): + if a or b \ + or c: + pass + + if a or \ + b or c: + pass diff --git a/src/formatter/snapshots/nodepaths/in.gd b/src/formatter/snapshots/nodepaths/in.gd new file mode 100644 index 0000000..0152af2 --- /dev/null +++ b/src/formatter/snapshots/nodepaths/in.gd @@ -0,0 +1,68 @@ +@onready var sprite: Sprite2D = %Sprite +@onready var sprites = [ %Sprite1,%Sprite2,%Sprite3 ] + +@onready var sprite_name = $Sprite +@onready var sprite_names = [$Sprite1, $Sprite2, $Sprite3] + +func f(): + print("$Sprite1", $Sprite1) + print("%Sprite1", %Sprite1) + var a=val % otherVal + +var a = $Child +var a = $Child/ GrandChild +var a = $Child/ GrandChild / GreatGrandChild +var a = $"../Sibling" +var a = $'../Sibling' +var a = $"../ Sibling " +var a = $' ../Sibling' +var a = $'..' # parent +var a = $"../.." # grandparent + +var a = get_node('Child') +var a = get_node("Child/Grand Child") +var a = get_node("../Sibling") + +if has_node('Child') and get_node('Child').has_node('GrandChild'): + pass + +var a = $%Unique +var a = $Child/%Unique +var a = $Child/ GrandChild/ %Unique +var a = $Child/%Unique/ChildOfUnique +var a = %Unique +var a = %Unique/Child +var a = %Unique/%UniqueChild + +var a = $"%Unique" +var a = get_node("%Unique") +var a = NodePath("%Unique") +var a = $'%Unique/Child' +var a = get_node('%Unique/Child') +var a = NodePath('%Unique/Child') +var a = $"%Unique/%UniqueChild" +var a = get_node("%Unique/%Unique Child") +var a = NodePath("%Unique/%Unique Child") + +if has_node('%Unique') and get_node('%Child').has_node('%GrandChild'): + pass + +var a = $badlyNamedChild +var a = $badlyNamedChild/badly_named_grandchild + +var a = NodePath("Child") +var a = NodePath('Child/GrandChild') +var a = NodePath('../Sibling') + +var a = get_node("Child").some_method() +var a = get_node("Child/GrandChild").some_method() +var a = get_node("%Child").some_method() +var a = $Child.some_method() +var a = $'Child'.some_method() +var a = $'%Child'.some_method() +var a = $Child/GrandChild.some_method() +var a = $"Child/GrandChild".some_method() +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() diff --git a/src/formatter/snapshots/nodepaths/out.gd b/src/formatter/snapshots/nodepaths/out.gd new file mode 100644 index 0000000..ea7f59d --- /dev/null +++ b/src/formatter/snapshots/nodepaths/out.gd @@ -0,0 +1,68 @@ +@onready var sprite: Sprite2D = %Sprite +@onready var sprites = [%Sprite1, %Sprite2, %Sprite3] + +@onready var sprite_name = $Sprite +@onready var sprite_names = [$Sprite1, $Sprite2, $Sprite3] + +func f(): + print("$Sprite1", $Sprite1) + print("%Sprite1", %Sprite1) + var a = val % otherVal + +var a = $Child +var a = $Child/GrandChild +var a = $Child/GrandChild/GreatGrandChild +var a = $"../Sibling" +var a = $'../Sibling' +var a = $"../ Sibling " +var a = $' ../Sibling' +var a = $'..' # parent +var a = $"../.." # grandparent + +var a = get_node('Child') +var a = get_node("Child/Grand Child") +var a = get_node("../Sibling") + +if has_node('Child') and get_node('Child').has_node('GrandChild'): + pass + +var a = $%Unique +var a = $Child/%Unique +var a = $Child/GrandChild/%Unique +var a = $Child/%Unique/ChildOfUnique +var a = %Unique +var a = %Unique/Child +var a = %Unique/%UniqueChild + +var a = $"%Unique" +var a = get_node("%Unique") +var a = NodePath("%Unique") +var a = $'%Unique/Child' +var a = get_node('%Unique/Child') +var a = NodePath('%Unique/Child') +var a = $"%Unique/%UniqueChild" +var a = get_node("%Unique/%Unique Child") +var a = NodePath("%Unique/%Unique Child") + +if has_node('%Unique') and get_node('%Child').has_node('%GrandChild'): + pass + +var a = $badlyNamedChild +var a = $badlyNamedChild/badly_named_grandchild + +var a = NodePath("Child") +var a = NodePath('Child/GrandChild') +var a = NodePath('../Sibling') + +var a = get_node("Child").some_method() +var a = get_node("Child/GrandChild").some_method() +var a = get_node("%Child").some_method() +var a = $Child.some_method() +var a = $'Child'.some_method() +var a = $'%Child'.some_method() +var a = $Child/GrandChild.some_method() +var a = $"Child/GrandChild".some_method() +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() diff --git a/src/formatter/snapshots/reserved-words/in.gd b/src/formatter/snapshots/reserved-words/in.gd new file mode 100644 index 0000000..96fdf96 --- /dev/null +++ b/src/formatter/snapshots/reserved-words/in.gd @@ -0,0 +1,7 @@ +func f(): + g(_a_signal_b) + g(_a_await_b) + g(_a_func_b) + g(_a_not_b) + g(_a_true_b) + g(_a_assert_b) diff --git a/src/formatter/snapshots/reserved-words/out.gd b/src/formatter/snapshots/reserved-words/out.gd new file mode 100644 index 0000000..96fdf96 --- /dev/null +++ b/src/formatter/snapshots/reserved-words/out.gd @@ -0,0 +1,7 @@ +func f(): + g(_a_signal_b) + g(_a_await_b) + g(_a_func_b) + g(_a_not_b) + g(_a_true_b) + g(_a_assert_b) diff --git a/src/formatter/snapshots/return-expression/in.gd b/src/formatter/snapshots/return-expression/in.gd new file mode 100644 index 0000000..1b40dcf --- /dev/null +++ b/src/formatter/snapshots/return-expression/in.gd @@ -0,0 +1,7 @@ +func f(): + return(some_value) + +func g(): return(some_value) + +func f(): + return func(): return false diff --git a/src/formatter/snapshots/return-expression/out.gd b/src/formatter/snapshots/return-expression/out.gd new file mode 100644 index 0000000..e29bfe1 --- /dev/null +++ b/src/formatter/snapshots/return-expression/out.gd @@ -0,0 +1,7 @@ +func f(): + return (some_value) + +func g(): return (some_value) + +func f(): + return func(): return false diff --git a/src/formatter/snapshots/return-type/in.gd b/src/formatter/snapshots/return-type/in.gd new file mode 100644 index 0000000..4f9e1b9 --- /dev/null +++ b/src/formatter/snapshots/return-type/in.gd @@ -0,0 +1,8 @@ +func f()->bool: + return (some_value) + +func g()-> void: + pass + +func f() ->void: + return func() -> bool: return false diff --git a/src/formatter/snapshots/return-type/out.gd b/src/formatter/snapshots/return-type/out.gd new file mode 100644 index 0000000..325e2d9 --- /dev/null +++ b/src/formatter/snapshots/return-type/out.gd @@ -0,0 +1,8 @@ +func f() -> bool: + return (some_value) + +func g() -> void: + pass + +func f() -> void: + return func() -> bool: return false diff --git a/src/formatter/snapshots/semicolon/in.gd b/src/formatter/snapshots/semicolon/in.gd new file mode 100644 index 0000000..635612d --- /dev/null +++ b/src/formatter/snapshots/semicolon/in.gd @@ -0,0 +1,3 @@ +func f(): + print(1);print(2); print(3); + print(4) diff --git a/src/formatter/snapshots/semicolon/out.gd b/src/formatter/snapshots/semicolon/out.gd new file mode 100644 index 0000000..1fd8802 --- /dev/null +++ b/src/formatter/snapshots/semicolon/out.gd @@ -0,0 +1,3 @@ +func f(): + print(1); print(2); print(3); + print(4) diff --git a/src/formatter/snapshots/strings/in.gd b/src/formatter/snapshots/strings/in.gd new file mode 100644 index 0000000..88ba8b8 --- /dev/null +++ b/src/formatter/snapshots/strings/in.gd @@ -0,0 +1,5 @@ +func f(): + var strings = [", ", "", " ", ", , "] + print("name: %s" % name) + print("%s / %s" % [a, b]) + print("%s/%s" % [a, b]) diff --git a/src/formatter/snapshots/strings/out.gd b/src/formatter/snapshots/strings/out.gd new file mode 100644 index 0000000..e8e5179 --- /dev/null +++ b/src/formatter/snapshots/strings/out.gd @@ -0,0 +1,5 @@ +func f(): + var strings = [", ", "", " ", ", , "] + print("name: %s" % name) + print("%s / %s" % [a, b]) + print("%s/%s" % [a, b]) diff --git a/src/formatter/tests/test1.in.gd b/src/formatter/tests/test1.in.gd deleted file mode 100644 index 1ee6ca7..0000000 --- a/src/formatter/tests/test1.in.gd +++ /dev/null @@ -1,5 +0,0 @@ -extends Node - - -func test (): - pass diff --git a/src/formatter/textmate.ts b/src/formatter/textmate.ts index 920b51c..3538656 100644 --- a/src/formatter/textmate.ts +++ b/src/formatter/textmate.ts @@ -3,7 +3,9 @@ import * as fs from "fs"; import * as vsctm from "vscode-textmate"; import * as oniguruma from "vscode-oniguruma"; import { keywords, symbols } from "./symbols"; -import { get_extension_uri } from "../utils"; +import { get_extension_uri, createLogger } from "../utils"; + +const log = createLogger("formatter.tm"); // Promisify readFile function readFile(path) { @@ -37,13 +39,18 @@ interface Token { // startIndex: number; // endIndex: number; scopes: string[]; + original: string; value: string; type?: string; param?: boolean; + string?: boolean; skip?: boolean; } function parse_token(token: Token) { + if (token.scopes.includes("string.quoted.gdscript")) { + token.string = true; + } if (token.scopes.includes("meta.function.parameters.gdscript")) { token.param = true; } @@ -86,14 +93,19 @@ function between(tokens: Token[], current: number) { if (prevToken.skip) return ""; if (nextToken.param) { + 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 " "; } @@ -101,6 +113,12 @@ function between(tokens: Token[], current: number) { } if (prev === "@") return ""; + if (prev === "-") { + if (tokens[current - 2]?.value === "(") { + return ""; + } + } + if (prev === ":" && next === "=") return ""; if (next === "(") { if (prev === "export") return ""; @@ -110,13 +128,18 @@ function between(tokens: Token[], current: number) { if (prev === ")" && nextToken.type === "keyword") return " "; + if (prev === "[" && nextToken.type === "symbol") return ""; if (prev === ":") return " "; if (prev === ";") return " "; if (prev === "#") return " "; if (next === "=") return " "; if (prev === "=") return " "; + if (tokens[current - 2]?.value === "=") { + if (["+", "-"].includes(prev)) return ""; + } if (prev === "(") return ""; if (next === "{") return " "; + if (next === "\\") return " "; if (next === "{}") return " "; if (prevToken?.type === "keyword") return " "; @@ -141,18 +164,24 @@ export function format_document(document: TextDocument): TextEdit[] { const edits: TextEdit[] = []; let lineTokens: vsctm.ITokenizeLineResult = null; + let onlyEmptyLinesSoFar = true; for (let lineNum = 0; lineNum < document.lineCount; lineNum++) { const line = document.lineAt(lineNum); // skip empty lines if (line.isEmptyOrWhitespace) { - // delete empty lines - if (lineNum === 0 || document.lineAt(lineNum - 1).isEmptyOrWhitespace) { - const range = new Range(lineNum, 0, lineNum + 1, 0); - edits.push(TextEdit.delete(range)); + // delete empty lines at the beginning of the file + if (onlyEmptyLinesSoFar) { + edits.push(TextEdit.delete(line.rangeIncludingLineBreak)); + } + // delete delete the current empty line if the next line is empty too + else if (lineNum < document.lineCount - 1 && document.lineAt(lineNum + 1).isEmptyOrWhitespace) { + edits.push(TextEdit.delete(line.rangeIncludingLineBreak)); } continue; } + onlyEmptyLinesSoFar = false; + // skip comments if (line.text[line.firstNonWhitespaceCharacterIndex] === "#") { continue; @@ -171,22 +200,25 @@ export function format_document(document: TextDocument): TextEdit[] { const tokens: Token[] = []; for (const t of lineTokens.tokens) { - const value = line.text.slice(t.startIndex, t.endIndex).trim(); - // skip whitespace tokens - if (value.trim() === "") { - continue; - } - const token: Token = { scopes: t.scopes, - value: value, + original: line.text.slice(t.startIndex, t.endIndex), + value: line.text.slice(t.startIndex, t.endIndex).trim(), }; - parse_token(token); + // skip whitespace tokens + if (!token.string && token.value.trim() === "") { + continue; + } tokens.push(token); } for (let i = 0; i < tokens.length; i++) { - nextLine += between(tokens, i) + tokens[i].value; + // log.debug(i, tokens[i].value, tokens[i]); + 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(); + } } edits.push(TextEdit.replace(line.range, nextLine)); diff --git a/syntaxes/GDScript.tmLanguage.json b/syntaxes/GDScript.tmLanguage.json index 3103e0f..025e150 100644 --- a/syntaxes/GDScript.tmLanguage.json +++ b/syntaxes/GDScript.tmLanguage.json @@ -3,49 +3,71 @@ "scopeName": "source.gdscript", "name": "GDScript", "patterns": [ - { "include": "#nodepath_object" }, - { "include": "#base_expression" }, - { "include": "#logic_op" }, - { "include": "#in_keyword" }, - { "include": "#getter_setter_godot4" }, - { "include": "#compare_op" }, - { "include": "#arithmetic_op" }, - { "include": "#assignment_op" }, - { "include": "#lambda_declaration" }, - { "include": "#control_flow" }, - { "include": "#annotations" }, - { "include": "#keywords" }, - { "include": "#self" }, - { "include": "#class_definition" }, - { "include": "#variable_definition" }, - { "include": "#class_name" }, - { "include": "#builtin_func" }, - { "include": "#builtin_get_node_shorthand" }, - { "include": "#builtin_classes" }, - { "include": "#const_vars" }, - { "include": "#pascal_case_class" }, - { "include": "#class_new" }, - { "include": "#class_is" }, - { "include": "#class_enum" }, - { "include": "#signal_declaration_bare" }, - { "include": "#signal_declaration" }, - { "include": "#function_declaration" }, - { "include": "#function_keyword" }, - { "include": "#any_method" }, - { "include": "#any_variable" }, - { "include": "#any_property" }, - { "include": "#extends" } + { "include": "#statement" }, + { "include": "#expression" } ], "repository": { + "statement": { + "patterns": [ { "include": "#extends_statement" } ] + }, + "statement_keyword": { + "patterns": [ + { + "name": "keyword.control.flow.gdscript", + "match": "(?x)\n \\b(?=|==|<|>|!=", "name": "keyword.operator.comparison.gdscript" }, - "arithmetic_op": { - "match": "\\+=|-=|\\*=|/=|%=|&=|\\|=|\\*|/|%|\\+|-|<<|>>|&|\\||\\^|~|!", + "arithmetic_operator": { + "match": "->|\\+=|-=|\\*=|/=|%=|&=|\\|=|\\*|/|%|\\+|-|<<|>>|&|\\||\\^|~|!", "name": "keyword.operator.arithmetic.gdscript" }, - "assignment_op": { + "assignment_operator": { "match": "=", "name": "keyword.operator.assignment.gdscript" }, @@ -172,7 +192,7 @@ "name": "keyword.control.gdscript" }, "keywords": { - "match": "\\b(?:class|class_name|extends|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|rpc|sync|remote|master|puppet|slave|remotesync|mastersync|puppetsync|trait|namespace)\\b", "name": "keyword.language.gdscript" }, "letter": { @@ -190,32 +210,38 @@ "name": "constant.numeric.integer.hexadecimal.gdscript" }, { - "match": "[-]?([0-9_]+\\.[0-9_]*(e[\\-\\+]?[0-9_]+)?)", + "match": "[-]?([0-9][0-9_]+\\.[0-9_]*(e[\\-\\+]?[0-9_]+)?)", "name": "constant.numeric.float.gdscript" }, { - "match": "[-]?(\\.[0-9_]+(e[\\-\\+]?[0-9_]+)?)", + "match": "[-]?(\\.[0-9][0-9_]*(e[\\-\\+]?[0-9_]+)?)", "name": "constant.numeric.float.gdscript" }, { - "match": "[-]?([0-9_]+e[\\-\\+]?\\[0-9_])", + "match": "[-]?([0-9][0-9_]*e[\\-\\+]?\\[0-9_])", "name": "constant.numeric.float.gdscript" }, { - "match": "[-]?[0-9_]+", + "match": "[-]?[0-9][0-9_]*", "name": "constant.numeric.integer.gdscript" } ] }, - "variable_definition": { - "begin": "\\b(?:(var)|(const))\\s+([a-zA-Z_]\\w*)\\s*", - "end": "$|;", + "variable_declaration": { + "name": "meta.variable.gdscript", + "begin": "\\b(?:(var)|(const))\\s+(?:(\\b[A-Z_][A-Z_0-9]*\\b)|([A-Za-z_]\\w*))\\s*", "beginCaptures": { "1": { "name": "keyword.language.gdscript storage.type.var.gdscript" }, "2": { "name": "keyword.language.gdscript storage.type.const.gdscript" }, - "3": { "name": "variable.other.gdscript" } + "3": { "name": "constant.language.gdscript" }, + "4": { "name": "variable.other.gdscript" } }, + "end": "$|;", "patterns": [ + { + "match": ":=|=(?!=)", + "name": "keyword.operator.assignment.gdscript" + }, { "match": "(:)\\s*([a-zA-Z_]\\w*)?", "captures": { @@ -223,10 +249,6 @@ "2": { "name": "entity.name.type.class.gdscript" } } }, - { - "match": "=(?!=)", - "name": "keyword.operator.assignment.gdscript" - }, { "match": "(setget)\\s+([a-zA-Z_]\\w*)(?:[,]\\s*([a-zA-Z_]\\w*))?", "captures": { @@ -235,7 +257,7 @@ "3": { "name": "entity.name.function.gdscript" } } }, - { "include": "#base_expression" }, + { "include": "#expression" }, { "include": "#letter" }, { "include": "#any_variable" }, { "include": "#any_property" }, @@ -255,19 +277,12 @@ "beginCaptures": { "1": { "name": "entity.name.function.gdscript" } }, "patterns": [ { "include": "#parameters" }, - { "include": "#line_continuation" }, - { - "match": "\\s*(\\-\\>)\\s*([a-zA-Z_]\\w*)\\s*\\:", - "captures": { - "1": { }, - "2": { "name": "entity.name.type.class.gdscript" } - } - } + { "include": "#line_continuation" } ] } ] }, - "class_definition": { + "class_declaration": { "match": "(?<=^class)\\s+([a-zA-Z_]\\w*)\\s*(?=:)", "captures": { "1": { "name": "entity.name.type.class.gdscript" }, @@ -290,26 +305,18 @@ } }, "class_enum": { + "match": "\\b([A-Z][a-zA-Z_0-9]*)\\.([A-Z_0-9]+)", "captures": { "1": { "name": "entity.name.type.class.gdscript" }, "2": { "name": "constant.language.gdscript" } - }, - "match": "\\b([A-Z][a-zA-Z_0-9]*)\\.([A-Z_0-9]+)" + } }, "class_name": { + "match": "(?<=class_name)\\s+([a-zA-Z_]\\w*(\\.([a-zA-Z_]\\w*))?)", "captures": { "1": { "name": "entity.name.type.class.gdscript" }, "2": { "name": "class.other.gdscript" } - }, - "match": "(?<=class_name)\\s+([a-zA-Z_]\\w*(\\.([a-zA-Z_]\\w*))?)" - }, - "extends": { - "match": "(?<=extends)\\s+[a-zA-Z_]\\w*(\\.([a-zA-Z_]\\w*))?", - "name": "entity.other.inherited-class.gdscript" - }, - "builtin_func": { - "match": "(?)\\s*([a-zA-Z_]\\w*)\\s*\\:", - "captures": { - "1": { }, - "2": { "name": "entity.name.type.class.gdscript" } - } - } + { "include": "#line_continuation" } ] }, "lambda_declaration": { "name": "meta.function.gdscript", "begin": "(func)\\s?(?=\\()", - "end": "(:|(?=[#'\"\\n]))", "beginCaptures": { "1": { "name": "keyword.language.gdscript storage.type.function.gdscript" }, "2": { "name": "entity.name.function.gdscript" } }, + "end": "(:|(?=[#'\"\\n]))", + "end2": "(\\s*(\\-\\>)\\s*(void\\w*)|([a-zA-Z_]\\w*)\\s*\\:)", + "endCaptures2": { + "1": { "name": "punctuation.separator.annotation.result.gdscript" }, + "2": { "name": "keyword.language.void.gdscript" }, + "3": { "name": "entity.name.type.class.gdscript markup.italic" } + }, "patterns": [ { "include": "#parameters" }, { "include": "#line_continuation" }, - { - "match": "\\s*(?:\\-\\>)\\s*(void\\w*)|([a-zA-Z_]\\w*)\\s*\\:", - "captures": { - "1": { "name": "keyword.language.void.gdscript" }, - "2": { "name": "entity.name.type.class.gdscript" } - } - }, { "include": "#base_expression" }, { "include": "#any_variable" }, { "include": "#any_property" } @@ -422,29 +422,23 @@ "function_declaration": { "name": "meta.function.gdscript", "begin": "(?x) \\s*\n (func) \\s+\n ([a-zA-Z_]\\w*) \\s*\n (?=\\()", - "end": "((:)|(?=[#'\"\\n]))", "beginCaptures": { "1": { "name": "keyword.language.gdscript storage.type.function.gdscript" }, "2": { "name": "entity.name.function.gdscript" } }, - "endCaptures": { "1": { "name": "punctuation.section.function.begin.gdscript" } }, + "end": "(:|(?=[#'\"\\n]))", + "end2": "(\\s*(\\-\\>)\\s*(void\\w*)|([a-zA-Z_]\\w*)\\s*\\:)", + "endCaptures2": { + "1": { "name": "punctuation.separator.annotation.result.gdscript" }, + "2": { "name": "keyword.language.void.gdscript" }, + "3": { "name": "entity.name.type.class.gdscript markup.italic" } + }, "patterns": [ { "include": "#parameters" }, { "include": "#line_continuation" }, - { - "match": "\\s*(?:\\-\\>)\\s*(void\\w*)|([a-zA-Z_]\\w*)\\s*\\:", - "captures": { - "1": { "name": "keyword.language.void.gdscript" }, - "2": { "name": "entity.name.type.class.gdscript" } - } - }, { "include": "#base_expression" } ] }, - "function_keyword": { - "match": "func", - "name": "keyword.language.gdscript" - }, "parameters": { "name": "meta.function.parameters.gdscript", "begin": "(\\()", @@ -472,12 +466,13 @@ "patterns": [ { "include": "#base_expression" } ] }, "annotated_parameter": { - "begin": "(?x)\n \\b\n ([a-zA-Z_]\\w*) \\s* (:)\n", - "end": "(,)|(?=\\))", + "begin": "(?x)\n \\s* ([a-zA-Z_]\\w*) \\s* (:)\\s* ([a-zA-Z_]\\w*)? \n", "beginCaptures": { "1": { "name": "variable.parameter.function.language.gdscript" }, - "2": { "name": "punctuation.separator.annotation.gdscript" } + "2": { "name": "punctuation.separator.annotation.gdscript" }, + "3": { "name": "entity.name.type.class.builtin.gdscript" } }, + "end": "(,)|(?=\\))", "endCaptures": { "1": { "name": "punctuation.separator.parameters.gdscript" } }, "patterns": [ { "include": "#base_expression" }, @@ -523,16 +518,18 @@ "name": "variable.other.gdscript" }, "any_property": { - "match": "\\b(\\.)\\s*(?