Skip to content

Commit fcd3f6f

Browse files
committed
Apply pascal to pascal case conversion that Godot uses
1 parent 2f21e26 commit fcd3f6f

File tree

1 file changed

+118
-1
lines changed

1 file changed

+118
-1
lines changed

addons/csharp_gdextension_bindgen/csharp_gdextension_bindgen.gd

Lines changed: 118 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,46 @@ const StringNameTypeName = {
2323
StringNameType.METHOD_NAME: "MethodName",
2424
StringNameType.SIGNAL_NAME: "SignalName",
2525
}
26+
const PASCAL_CASE_NAME_OVERRIDES = {
27+
"BitMap": "Bitmap",
28+
"JSONRPC": "JsonRpc",
29+
"Object": "GodotObject",
30+
"OpenXRIPBinding": "OpenXRIPBinding",
31+
"SkeletonModification2DCCDIK": "SkeletonModification2DCcdik",
32+
"SkeletonModification2DFABRIK": "SkeletonModification2DFabrik",
33+
"SkeletonModification3DCCDIK": "SkeletonModification3DCcdik",
34+
"SkeletonModification3DFABRIK": "SkeletonModification3DFabrik",
35+
"System": "System_",
36+
"Thread": "GodotThread",
37+
}
38+
const PASCAL_CASE_PART_OVERRIDES = {
39+
"AA": "AA", # Anti Aliasing
40+
"AO": "AO", # Ambient Occlusion
41+
"FILENAME": "FileName",
42+
"FADEIN": "FadeIn",
43+
"FADEOUT": "FadeOut",
44+
"FX": "FX",
45+
"GI": "GI", # Global Illumination
46+
"GZIP": "GZip",
47+
"HBOX": "HBox", # Horizontal Box
48+
"ID": "Id",
49+
"IO": "IO", # Input/Output
50+
"IP": "IP", # Internet Protocol
51+
"IV": "IV", # Initialization Vector
52+
"MACOS": "MacOS",
53+
"NODEPATH": "NodePath",
54+
"SPIRV": "SpirV",
55+
"STDIN": "StdIn",
56+
"STDOUT": "StdOut",
57+
"USERNAME": "UserName",
58+
"UV": "UV",
59+
"UV2": "UV2",
60+
"VBOX": "VBox", # Vertical Box
61+
"WHITESPACE": "WhiteSpace",
62+
"WM": "WM",
63+
"XR": "XR",
64+
"XRAPI": "XRApi",
65+
}
2666

2767

2868
func _enter_tree():
@@ -549,7 +589,7 @@ static func _get_property_type(cls_name: StringName, property: Dictionary) -> St
549589
return "Godot.Rid"
550590
TYPE_OBJECT:
551591
if property["class_name"] and property["class_name"] != "Object":
552-
return _get_class_from_class_name(property["class_name"])
592+
return _pascal_to_pascal_case(_get_class_from_class_name(property["class_name"]))
553593
else:
554594
return "GodotObject"
555595
TYPE_ARRAY:
@@ -730,3 +770,80 @@ static func _needs_enum_suffix(cls_name: StringName, enum_name: String) -> bool:
730770
if snake_case_enum_name == property["name"]:
731771
return true
732772
return false
773+
774+
775+
# Pascal case conversion used for class names.
776+
# Replicates the logic from `godot/modules/mono/utils/naming_utils.cpp`
777+
static func _is_ascii_upper_case(c: String) -> bool:
778+
return c.to_upper() == c
779+
780+
781+
static func _is_ascii_lower_case(c: String) -> bool:
782+
return c.to_lower() == c
783+
784+
785+
static func _is_digit(c: String) -> bool:
786+
return c >= "0" and c <= "9"
787+
788+
789+
static func _split_pascal_case(p_identifier: String) -> PackedStringArray:
790+
var parts := PackedStringArray()
791+
var current_part_start := 0
792+
var prev_was_upper := _is_ascii_upper_case(p_identifier[0])
793+
for i in range(1, p_identifier.length()):
794+
if prev_was_upper:
795+
if _is_digit(p_identifier[i]) or _is_ascii_lower_case(p_identifier[i]):
796+
if not _is_digit(p_identifier[i]):
797+
# These conditions only apply when the separator is not a digit.
798+
if i - current_part_start == 1:
799+
# Upper character was only the beginning of a word.
800+
prev_was_upper = false
801+
continue
802+
if i != p_identifier.length():
803+
# If this is not the last character, the last uppercase
804+
# character is the start of the next word.
805+
i -= 1
806+
if i - current_part_start > 0:
807+
parts.append(p_identifier.substr(current_part_start, i - current_part_start))
808+
current_part_start = i
809+
prev_was_upper = false
810+
else:
811+
if _is_digit(p_identifier[i]) or _is_ascii_upper_case(p_identifier[i]):
812+
parts.append(p_identifier.substr(current_part_start, i - current_part_start))
813+
current_part_start = i
814+
prev_was_upper = true
815+
816+
# Add the rest of the identifier as the last part.
817+
if current_part_start != p_identifier.length():
818+
parts.append(p_identifier.substr(current_part_start))
819+
return parts
820+
821+
822+
static func _pascal_to_pascal_case(p_identifier: String) -> String:
823+
if p_identifier.length() == 0:
824+
return p_identifier
825+
if p_identifier.length() <= 2:
826+
return p_identifier.to_upper()
827+
if PASCAL_CASE_NAME_OVERRIDES.has(p_identifier):
828+
return PASCAL_CASE_NAME_OVERRIDES[p_identifier]
829+
830+
var parts := _split_pascal_case(p_identifier)
831+
var ret := ""
832+
for part in parts:
833+
if PASCAL_CASE_PART_OVERRIDES.has(part):
834+
ret += PASCAL_CASE_PART_OVERRIDES[part]
835+
continue
836+
837+
if part.length() <= 2 and _is_ascii_upper_case(part):
838+
ret += part.to_upper()
839+
continue
840+
841+
part[0] = part[0].to_upper()
842+
for i in range(1, part.length()):
843+
if _is_digit(part[i - 1]):
844+
# Use uppercase after digits.
845+
part[i] = part[i].to_upper()
846+
else:
847+
part[i] = part[i].to_lower()
848+
ret += part
849+
return ret

0 commit comments

Comments
 (0)