Skip to content

Commit

Permalink
Handle inner classes super calls in constructor with arguments
Browse files Browse the repository at this point in the history
  • Loading branch information
adamuso committed Dec 19, 2021
1 parent a544f34 commit 1e9f87f
Show file tree
Hide file tree
Showing 3 changed files with 218 additions and 26 deletions.
46 changes: 22 additions & 24 deletions parse_node/parse_class_declaration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,30 +145,28 @@ class InnerTest extends Node2D:
`,
}

// TODO: implement calling super on inner class

// export const testInnerClassExtendsSuperCall: Test = {
// ts: `
// export default class Test {
// }

// export class InnerTest extends Node2D {
// field: int = 2;

// constructor() {
// super();
// }
// }
// `,
// expected: `
// class_name Test

// class InnerTest extends Node2D:
// var field: int = 2
// func _init().():
// pass
// `,
// }
export const testInnerClassExtendsSuperCall: Test = {
ts: `
export default class Test {
}
export class InnerTest extends Node2D {
field: int = 2;
constructor() {
super();
}
}
`,
expected: `
class_name Test
class InnerTest extends Node2D:
var field: int = 2
func _init().():
pass
`,
}

export const testFileWithoutDefaultClass: Test = {
ts: `
Expand Down
193 changes: 192 additions & 1 deletion parse_node/parse_constructor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,87 @@ import ts from "typescript"
import { ParseState, combine } from "../parse_node"

import { ParseNodeType } from "../parse_node"
import { Test } from "../tests/test"

export const parseConstructor = (
node: ts.ConstructorDeclaration,
props: ParseState
): ParseNodeType => {
const modifiers = (node.parent.modifiers ?? []).map((v) => v.getText())

const constructorArgs = combine({
parent: node,
nodes: node.parameters,
props,
addIndent: false,
parsedStrings: (...args: string[]) => {
return args.join(", ")
},
})

if (
ts.isClassDeclaration(node.parent) &&
!modifiers.includes("declare") &&
!modifiers.includes("default")
) {
// Handle inner class constructor

if (node.body) {
// Find super call
const superCallStatement = node.body.statements.find(
(stmt) =>
ts.isExpressionStatement(stmt) &&
ts.isCallExpression(stmt.expression) &&
stmt.expression.expression.kind === ts.SyntaxKind.SuperKeyword
)

if (superCallStatement) {
const superCall = (superCallStatement as ts.ExpressionStatement)
.expression as ts.CallExpression

const superCallArgs = combine({
parent: superCall,
nodes: superCall.arguments,
props,
addIndent: false,
parsedStrings: (...args: string[]) => {
return args.join(", ")
},
})

return combine({
parent: node,
nodes: node.body,
props,
addIndent: true,
parsedStrings: (body) => `
func _init(${constructorArgs.content}).(${superCallArgs.content}):
${body.trim().length > 0 ? body : "pass"}
`,
})
}

// The trim() is for a constructor with only one element: a super() call
return combine({
parent: node,
nodes: node.body,
props,
addIndent: true,
parsedStrings: (body) => `
func _init(${constructorArgs.content}):
${body.trim().length > 0 ? body : "pass"}
`,
})
} else {
return combine({
parent: node,
nodes: [],
props,
parsedStrings: () => `func _init():\n pass`,
})
}
}

if (node.body) {
// The trim() is for a constructor with only one element: a super() call

Expand All @@ -25,7 +101,122 @@ func _ready():
parent: node,
nodes: [],
props,
parsedStrings: () => `func _ready():\n pass`,
parsedStrings: () => `func _ready():\n pass`,
})
}
}

export const testConstructorNoBody: Test = {
ts: `
export default class Test {
constructor();
}
`,
expected: `
class_name Test
func _ready():
pass
`,
}

export const testConstructorEmptyBody: Test = {
ts: `
export default class Test {
constructor() {
}
}
`,
expected: `
class_name Test
func _ready():
pass
`,
}

export const testConstructor: Test = {
ts: `
export default class Test {
constructor() {
print("Hello");
}
}
`,
expected: `
class_name Test
func _ready():
print("Hello")
`,
}

export const testInnerClassConstructorEmptyBody: Test = {
ts: `
export class Test {
constructor() {
}
}
`,
expected: `
class Test:
func _init():
pass
`,
}

export const testExtendedInnerClassConstructorNoBody: Test = {
ts: `
export class Test extends Node2D {
constructor();
}
`,
expected: `
class Test extends Node2D:
func _init():
pass
`,
}

export const testExtendedInnerClassConstructor: Test = {
ts: `
export class Test extends Node2D {
constructor() {
super();
print("Hello");
}
}
`,
expected: `
class Test extends Node2D:
func _init().():
print("Hello")
`,
}

export const testExtendedInnerClassConstructorWithArguments: Test = {
ts: `
class Base extends Node2D {
constructor(name: string) {
super();
print(name);
}
}
export class Test extends Base {
constructor() {
super("TestName");
}
}
`,
expected: `
class Base extends Node2D:
func _init(name: String).():
print(name)
class Test extends Base:
func _init().("TestName"):
pass
`,
}
5 changes: 4 additions & 1 deletion parse_node/parse_source_file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,11 @@ const getClassDeclarationHeader = (
) {
// If a class declaration does not have default export then this is an inner class
// The syntax for extending inner class in gdscript is: extends "res://compiled/Test.gd".BaseType

extendsFrom = asset
? `"${asset.resPath}".${type.getText()}`
? props.sourceFileAsset === asset
? type.getText()
: `"${asset.resPath}".${type.getText()}`
: "[missing]"
} else if (!modifiers.includes("declare") && !classDecl.name) {
// If a class declaration have default export and does not have a name then it is anonymous
Expand Down

0 comments on commit 1e9f87f

Please sign in to comment.