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

Expose String.validate_identifier method #67701

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
22 changes: 15 additions & 7 deletions core/string/ustring.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions core/variant/variant_call.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
11 changes: 11 additions & 0 deletions doc/classes/String.xml
Original file line number Diff line number Diff line change
Expand Up @@ -437,11 +437,14 @@
<description>
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.
</description>
</method>
<method name="is_valid_int" qualifiers="const">
Expand Down Expand Up @@ -1002,6 +1005,14 @@
[/codeblocks]
</description>
</method>
<method name="validate_identifier" qualifiers="const">
<return type="String" />
<description>
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.
</description>
</method>
<method name="validate_node_name" qualifiers="const">
<return type="String" />
<description>
Expand Down
11 changes: 11 additions & 0 deletions doc/classes/StringName.xml
Original file line number Diff line number Diff line change
Expand Up @@ -412,11 +412,14 @@
<description>
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.
</description>
</method>
<method name="is_valid_int" qualifiers="const">
Expand Down Expand Up @@ -909,6 +912,14 @@
[/codeblocks]
</description>
</method>
<method name="validate_identifier" qualifiers="const">
<return type="String" />
<description>
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.
</description>
</method>
<method name="validate_node_name" qualifiers="const">
<return type="String" />
<description>
Expand Down
19 changes: 2 additions & 17 deletions editor/connections_dialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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();
}

/*
Expand Down
20 changes: 16 additions & 4 deletions tests/core/string/test_string.h
Original file line number Diff line number Diff line change
Expand Up @@ -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") {
Expand Down