diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp
index adab6d07c7f0..05d04079a7f7 100644
--- a/core/string/ustring.cpp
+++ b/core/string/ustring.cpp
@@ -3805,20 +3805,28 @@ static _FORCE_INLINE_ bool _is_valid_identifier_bit(int p_index, char32_t p_char
String String::validate_identifier() const {
if (is_empty()) {
- return "_"; // Empty string is not a valid identifier;
+ return "_"; // Empty string is not a valid identifier.
}
- String result = *this;
- int len = result.length();
- char32_t *buffer = result.ptrw();
+ String new_string;
+ int len = length();
for (int i = 0; i < len; i++) {
- if (!_is_valid_identifier_bit(i, buffer[i])) {
- buffer[i] = '_';
+ const char32_t c = operator[](i);
+ if (is_ascii_identifier_char(c)) {
+ new_string += c;
}
}
- return result;
+ if (new_string.is_empty()) {
+ return "_";
+ }
+
+ if (is_digit(new_string[0])) {
+ return "_" + new_string;
+ }
+
+ return new_string;
}
bool String::is_valid_identifier() const {
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index ac569941bf01..778b7eaa82b7 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -1692,6 +1692,7 @@ static void _register_variant_builtin_methods() {
bind_string_method(c_unescape, sarray(), varray());
bind_string_method(json_escape, sarray(), varray());
+ bind_string_method(validate_identifier, sarray(), varray());
bind_string_method(validate_node_name, sarray(), varray());
bind_string_method(is_valid_identifier, sarray(), varray());
diff --git a/doc/classes/String.xml b/doc/classes/String.xml
index 97466e786038..d08637b79bba 100644
--- a/doc/classes/String.xml
+++ b/doc/classes/String.xml
@@ -437,11 +437,14 @@
Returns [code]true[/code] if this string is a valid identifier. A valid identifier may contain only letters, digits and underscores ([code]_[/code]), and the first character may not be a digit.
[codeblock]
+ print("".is_valid_identifier()) # Prints false
+ print("_".is_valid_identifier()) # Prints true
print("node_2d".is_valid_identifier()) # Prints true
print("TYPE_FLOAT".is_valid_identifier()) # Prints true
print("1st_method".is_valid_identifier()) # Prints false
print("MyMethod#2".is_valid_identifier()) # Prints false
[/codeblock]
+ [b]Note:[/b] [code]_[/code] is not a valid identifier in GDScript.
@@ -1002,6 +1005,14 @@
[/codeblocks]
+
+
+
+ Removes any characters from the string that are prohibited in identifier names (see [method is_valid_identifier]). If the string is empty, returns [code]_[/code]. If the string begins with a digit, the [code]_[/code] prefix will be added.
+ [b]Note:[/b] [code]_[/code] is not a valid identifier in GDScript.
+ [b]Note:[/b] This method removes spaces. Use [code]replace(" ", "_")[/code] or [method to_snake_case] before [code]validate_identifier()[/code] if needed.
+
+
diff --git a/doc/classes/StringName.xml b/doc/classes/StringName.xml
index b46e39b8d7fb..0b2db78b894d 100644
--- a/doc/classes/StringName.xml
+++ b/doc/classes/StringName.xml
@@ -412,11 +412,14 @@
Returns [code]true[/code] if this string is a valid identifier. A valid identifier may contain only letters, digits and underscores ([code]_[/code]), and the first character may not be a digit.
[codeblock]
+ print("".is_valid_identifier()) # Prints false
+ print("_".is_valid_identifier()) # Prints true
print("node_2d".is_valid_identifier()) # Prints true
print("TYPE_FLOAT".is_valid_identifier()) # Prints true
print("1st_method".is_valid_identifier()) # Prints false
print("MyMethod#2".is_valid_identifier()) # Prints false
[/codeblock]
+ [b]Note:[/b] [code]_[/code] is not a valid identifier in GDScript.
@@ -909,6 +912,14 @@
[/codeblocks]
+
+
+
+ Removes any characters from the string that are prohibited in identifier names (see [method is_valid_identifier]). If the string is empty, returns [code]_[/code]. If the string begins with a digit, the [code]_[/code] prefix will be added.
+ [b]Note:[/b] [code]_[/code] is not a valid identifier in GDScript.
+ [b]Note:[/b] This method removes spaces. Use [code]replace(" ", "_")[/code] or [method to_snake_case] before [code]validate_identifier()[/code] if needed.
+
+
diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp
index 1f0cc1dc77b7..21f1997b8ad5 100644
--- a/editor/connections_dialog.cpp
+++ b/editor/connections_dialog.cpp
@@ -214,22 +214,7 @@ void ConnectDialog::_remove_bind() {
* Automatically generates a name for the callback method.
*/
StringName ConnectDialog::generate_method_callback_name(Node *p_source, String p_signal_name, Node *p_target) {
- String node_name = p_source->get_name();
- for (int i = 0; i < node_name.length(); i++) { // TODO: Regex filter may be cleaner.
- char32_t c = node_name[i];
- if (!is_ascii_identifier_char(c)) {
- if (c == ' ') {
- // Replace spaces with underlines.
- c = '_';
- } else {
- // Remove any other characters.
- node_name.remove_at(i);
- i--;
- continue;
- }
- }
- node_name[i] = c;
- }
+ String node_name = String(p_source->get_name()).replace("-", "_");
Dictionary subst;
subst["NodeName"] = node_name.to_pascal_case();
@@ -247,7 +232,7 @@ StringName ConnectDialog::generate_method_callback_name(Node *p_source, String p
dst_method = String(EDITOR_GET("interface/editors/default_signal_callback_name")).format(subst);
}
- return dst_method;
+ return dst_method.validate_identifier();
}
/*
diff --git a/tests/core/string/test_string.h b/tests/core/string/test_string.h
index 659d451d76e6..7b2dcee1cdac 100644
--- a/tests/core/string/test_string.h
+++ b/tests/core/string/test_string.h
@@ -1678,14 +1678,26 @@ TEST_CASE("[String] validate_identifier") {
String empty_string;
CHECK(empty_string.validate_identifier() == "_");
- String numeric_only = "12345";
- CHECK(numeric_only.validate_identifier() == "_2345");
+ String valid_identifier = "abc_123_Test4";
+ CHECK(valid_identifier.validate_identifier() == "abc_123_Test4");
+
+ String numeric_prefix = "123abc";
+ CHECK(numeric_prefix.validate_identifier() == "_123abc");
+
+ String invalid_prefix = String::utf8(":@*#&世界abc");
+ CHECK(invalid_prefix.validate_identifier() == "abc");
+
+ String invalid_and_numeric_prefix = String::utf8(":@*#&世界123abc");
+ CHECK(invalid_and_numeric_prefix.validate_identifier() == "_123abc");
String name_with_spaces = "Name with spaces";
- CHECK(name_with_spaces.validate_identifier() == "Name_with_spaces");
+ CHECK(name_with_spaces.validate_identifier() == "Namewithspaces");
String name_with_invalid_chars = String::utf8("Invalid characters:@*#&世界");
- CHECK(name_with_invalid_chars.validate_identifier() == "Invalid_characters_______");
+ CHECK(name_with_invalid_chars.validate_identifier() == "Invalidcharacters");
+
+ String invalid_chars_only = String::utf8(":@*#&世界");
+ CHECK(invalid_chars_only.validate_identifier() == "_");
}
TEST_CASE("[String] Variant indexed get") {