Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make export default class the main file class in GDScript, add support for subclasses #61

Open
wants to merge 29 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
946cb1e
Proof of concept, export default is the main file class, others are s…
adamuso Dec 17, 2021
ddcea40
Fix dynamic defs generation and adjust test to use export default, al…
adamuso Dec 18, 2021
2753c88
Fix enum importing without file separation
adamuso Dec 18, 2021
b558ecd
Change inner classes import to preloading
adamuso Dec 18, 2021
5b3418e
Handle extending inner classes from other files
adamuso Dec 18, 2021
6687a9c
Implement anonymous classes
adamuso Dec 18, 2021
5894a26
Fix not taking into account declare classes
adamuso Dec 18, 2021
a544f34
Cleanup tests
adamuso Dec 18, 2021
1e9f87f
Handle inner classes super calls in constructor with arguments
adamuso Dec 19, 2021
bf51a77
Merge remote-tracking branch 'origin/HEAD' into support-for-multiple-…
adamuso Dec 19, 2021
1825ef2
Cleanup commented out code
adamuso Dec 19, 2021
e72f177
Merge branch 'johnfn:main' into support-for-multiple-classes-in-one-file
adamuso Dec 19, 2021
8381679
Fix node path def generation, refactor extending inner/anonymous classes
adamuso Dec 20, 2021
6030bc9
Fix not showing autoload error on non default class
adamuso Dec 21, 2021
ff497da
Update readme
adamuso Dec 21, 2021
c80d179
Merge branch 'main' into support-for-multiple-classes-in-one-file
adamuso Dec 21, 2021
c1f59dd
Fix tests after merging with main
adamuso Dec 21, 2021
f44ae4e
Merge commit '0d007f4b61ad131265d000ebda22f5708ba3ddbe' into support-…
adamuso Dec 23, 2021
fc82ee2
Merge remote-tracking branch 'origin/main' into support-for-multiple-…
adamuso Jan 4, 2022
060ec33
Change class declaration generation, single exported classes are now …
adamuso Jan 4, 2022
b43f26c
Adjust imports and dynamic defs generation
adamuso Jan 4, 2022
ced47d7
Add defs for main and inner attributes
adamuso Jan 4, 2022
e1ea6f2
Revert most test changes related to 'default'
adamuso Jan 4, 2022
b98306f
Revert accidental package-lock update and remove more default from tests
adamuso Jan 4, 2022
630d5ef
Update readme and main class check
adamuso Jan 4, 2022
a61f720
Update classes readme
adamuso Jan 15, 2022
751e2ee
Fix false positive tests when string is expected and add more autoloa…
adamuso Jan 15, 2022
4f65188
Make some error descriptions more user friendly
adamuso Jan 15, 2022
63756f6
Merge branch 'main' into support-for-multiple-classes-in-one-file
adamuso May 24, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@

163 changes: 145 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,132 @@ To compile all source files once:

## Details and Differences

### Classes

A single exported class in a file is always treated as the main GDScript class.

```ts
export class MyNode extends Node2D {
constructor() {
super()
print("Hello world")
}
}
```

```gdscript
extends Node2D
class_name MyNode

func _ready():
print("Hello world")
```

Classes marked `default` are the main class (the class that gets attached to the node when you add a script to a node in Godot). Other classes are inner classes in Godot. There is one exception to this rule: if you have only a single class in a file, that automatically becomes the main class, default or not.

```ts
export default class MyNode extends Node2D {
constructor() {
super()
print("Hello main class")
}
}

export class ExportedInnerClass {
constructor() {
print("Hello ExportedInnerClass")
}
}

class NotExportedInnerClass {
constructor() {
print("Hello NotExportedInnerClass")
}
}
```

```gdscript
extends Node2D
class_name MyNode

class ExportedInnerClass
func _init():
print("Hello ExportedInnerClass")

class NotExportedInnerClass
func _init():
print("Hello NotExportedInnerClass")

func _ready():
print("Hello main class")
```

If you prefer not to use `default`, you can also decorate the class with `@main`, which has the same effect. Again, this is only necessary if you are using multiple classes in a file.

```ts
@main
export class MainClass extends Node2D {
constructor() {
super()
print("Hello MainClass")
}
}

export class OtherClass {
constructor() {
print("Hello OtherClass")
}
}
```

```gdscript
extends Node2D
class_name MainClass

class OtherClass
func _init():
print("Hello OtherClass")

func _ready():
print("Hello MainClass")
```

If you want single class in a file to be an inner class in Godot then you need to explicitly mark it with `@inner` attribute.

```ts
@inner
export class MyNode extends Node2D {
constructor() {
super()
print("Hello world")
}
}
```

```gdscript
class MyNode extends Node2D:
func _init().():
print("Hello world")
```

If a class is marked `default`, it can be anonymous. This omits `class_name` generation.

```ts
export default class extends Node2D {
constructor() {
super()
print("Hello world")
}
}
```

```gdscript
extends Node2D

func _ready():
print("Hello world")
```

### `get_node`

`get_node` has been supercharged - it will now autocomplete the names of all
Expand All @@ -53,7 +179,7 @@ get type errors if you break any `get_node` calls!!

ts2gd also provides a way to get any node by name, even the ones it can't verify exist:

```
```ts
this.get_node<Label>("MyLabel")
```

Expand All @@ -73,13 +199,13 @@ Godot decides to put a bunch of enum values into global scope. I think this clut

For instance,

```
```gdscript
Input.is_key_pressed(KEY_W)
```

becomes

```
```ts
Input.is_key_pressed(KeyList.KEY_SPACE)
```

Expand All @@ -93,13 +219,13 @@ The RPC syntax has been improved.

GDScript:

```
```gdscript
this.rpc("my_rpc_method", "some-argument)
```

TypeScript:

```
```ts
this.my_rpc_method.rpc("some-argument")
```

Expand All @@ -111,7 +237,7 @@ Signals have been improved. All signals now start with `$` and are properties of

This is what connect looks like in ts2gd:

```
```ts
this.my_button.$pressed.connect(() => {
print("Clicked the button!)
})
Expand All @@ -129,7 +255,7 @@ yield this.get_tree().$idle_frame

This is what emit looks like in ts2gd:

```
```ts
class MySignallingClass extends Node2D {
$my_signal!: Signal // ! to avoid the TS error about this signal being unassigned

Expand All @@ -145,9 +271,10 @@ In order to make a class autoload, decorate your class with `@autoload`, and cre

Here's a full example of an autoload class.

```
```ts
// autoload can be specified only on a main class (marked 'export default' or with '@main' decorator)
@autoload
class MyAutoloadClass extends Node2D {
export default class MyAutoloadClass extends Node2D {
public hello = "hi"
}

Expand All @@ -158,8 +285,8 @@ export const MyAutoload = new MyAutoloadClass()

In order to mark an instance variable as `export`, use `@exports`, e.g.:

```
class ExportExample extends Node2D {
```ts
export default class ExportExample extends Node2D {
@exports
public hello = "exported"
}
Expand All @@ -169,9 +296,9 @@ class ExportExample extends Node2D {

In order to mark a script as `tool`, use `@tool`.

```
```ts
@tool
class MyToolScript extends Node2D {
export default class MyToolScript extends Node2D {
// ... do some tool script work here
}
```
Expand All @@ -184,7 +311,7 @@ To mark a method as remotesync or remote, use `@remotesync` and `@remote`, respe

TypeScript sadly has no support for operator overloading.

```
```ts
const v1 = Vector(1, 2)
const v2 = Vector(1, 2);

Expand All @@ -194,13 +321,13 @@ v1.mul(v2); // v1 * v2
v1.div(v2); // v1 / v2
```

The add/sub/mul/div gets compiled into the corresponding arithmatic.
The add/sub/mul/div gets compiled into the corresponding arithmetic.

### Dictionary

The default TS dictionary (e.g. `const dict = { a: 1 }`) only supports string, number and symbol as keys. If you want anything else, you can just use the Dictionary type, and use `.put` instead of square bracket access.

```
```ts
const myComplexDict: Dictionary<Node2D, int> = todict({})

myComplexDict.put(myNode, 5)
Expand All @@ -210,7 +337,7 @@ myComplexDict.put(myNode, 5)

If you'd like ts2gd to generate the latest TS definitions from Godot, clone the Godot repository and point it at the 3.x tag. Then add the following to your ts2gd.json:

```
```json
"godotSourceRepoPath": "/path/to/your/godot/clone"
```

Expand Down Expand Up @@ -274,7 +401,7 @@ Need something more customized? You can provide an array of [anymatch](https://w
- [x] strongly type input action names
- [x] handle renames better - delete the old compiled file, etc.
- [ ] refactoring class names doesn't really work right now because i think we need to rename types in tscn files...
- [ ] would be nice to declare multiple classes in the same .ts file and have the compiler sort it out
- [x] would be nice to declare multiple classes in the same .ts file and have the compiler sort it out
- [x] add a way to install ts2gd as a global command
- [x] ensure that signal arguments match up
- [ ] add a way to use ts2gd via installer rather than command line
Expand Down
2 changes: 2 additions & 0 deletions _godot_defs/static/@base.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ declare function exports(target: Node, name: string): void;
declare const export_flags: (...flags: any[]) => (target: Node, name: string) => void
declare function autoload(target: typeof Node): void
declare function tool(target: typeof Node): void;
declare function main(target: typeof Node): void;
declare function inner(target: typeof Node): void;

declare type int = number;
declare type float = number;
Expand Down
2 changes: 2 additions & 0 deletions generate_library_defs/generate_base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ declare function exports(target: Node, name: string): void;
declare const export_flags: (...flags: any[]) => (target: Node, name: string) => void
declare function autoload(target: typeof Node): void
declare function tool(target: typeof Node): void;
declare function main(target: typeof Node): void;
declare function inner(target: typeof Node): void;

declare type int = number;
declare type float = number;
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@
"semi": false
},
"lint-staged": {
"*.ts": [
"eslint --fix"
],
"*.{ts,json}": [
"eslint --fix",
"prettier --write"
]
},
Expand Down
1 change: 1 addition & 0 deletions parse_node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export type ParseState = {
* Is the current file we're in an autoload class?
*/
isAutoload: boolean
isMainClass: boolean
mostRecentControlStructureIsSwitch: boolean
mostRecentForStatement?: {
incrementor: string
Expand Down
6 changes: 3 additions & 3 deletions parse_node/parse_call_expression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ func b():
export const testArrowFunction: Test = {
ts: `
const test = () => 5;
test()
test()
`,
expected: `
func __gen(captures):
Expand Down Expand Up @@ -616,7 +616,7 @@ export class Test {
foo() {
let enem: any;

enem.$on_die.connect(() => { this.enemies.erase(enem) });
enem.$on_die.connect(() => { this.enemies.erase(enem) });
}
}
`,
Expand All @@ -628,7 +628,7 @@ func __gen(captures):
var enemies
func foo():
var enem
enem.connect("on_die", self, "__gen", [{"enem": enem}])
enem.connect("on_die", self, "__gen", [{"enem": enem}])
`,
}

Expand Down
Loading