summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDanil Alexeev <danil@alexeev.xyz>2022-08-30 12:36:24 +0300
committerDanil Alexeev <danil@alexeev.xyz>2022-08-30 12:36:24 +0300
commitd4555ef5fbf706c43faccf5fdb3f023696197727 (patch)
treeefc5600ae84f8827b800ca7bfd42333ecf38b542
parent432b25d3649319517827dbf7bc275e81e0a2b92e (diff)
Add `String.to_{camel,pascal,snake}_case` methods
-rw-r--r--core/string/ustring.cpp85
-rw-r--r--core/string/ustring.h5
-rw-r--r--core/variant/variant_call.cpp3
-rw-r--r--doc/classes/String.xml18
-rw-r--r--editor/connections_dialog.cpp22
-rw-r--r--editor/editor_autoload_settings.cpp6
-rw-r--r--editor/editor_node.cpp8
-rw-r--r--editor/plugins/bone_map_editor_plugin.cpp2
-rw-r--r--editor/plugins/script_text_editor.cpp2
-rw-r--r--editor/rename_dialog.cpp4
-rw-r--r--editor/script_create_dialog.cpp2
-rw-r--r--editor/shader_create_dialog.cpp2
-rwxr-xr-xeditor/translations/extract.py2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs9
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs39
-rw-r--r--modules/mono/glue/runtime_interop.cpp15
-rw-r--r--scene/main/node.cpp11
-rw-r--r--tests/core/string/test_string.h87
18 files changed, 234 insertions, 88 deletions
diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp
index ed3d4b0b54..d8b93998af 100644
--- a/core/string/ustring.cpp
+++ b/core/string/ustring.cpp
@@ -970,62 +970,71 @@ const char32_t *String::get_data() const {
return size() ? &operator[](0) : &zero;
}
-String String::capitalize() const {
- String aux = this->camelcase_to_underscore(true).replace("_", " ").strip_edges();
- String cap;
- for (int i = 0; i < aux.get_slice_count(" "); i++) {
- String slice = aux.get_slicec(' ', i);
- if (slice.length() > 0) {
- slice[0] = _find_upper(slice[0]);
- if (i > 0) {
- cap += " ";
- }
- cap += slice;
- }
- }
-
- return cap;
-}
-
-String String::camelcase_to_underscore(bool lowercase) const {
+String String::_camelcase_to_underscore() const {
const char32_t *cstr = get_data();
String new_string;
int start_index = 0;
for (int i = 1; i < this->size(); i++) {
- bool is_upper = is_ascii_upper_case(cstr[i]);
- bool is_number = is_digit(cstr[i]);
+ bool is_prev_upper = is_ascii_upper_case(cstr[i - 1]);
+ bool is_prev_lower = is_ascii_lower_case(cstr[i - 1]);
+ bool is_prev_digit = is_digit(cstr[i - 1]);
- bool are_next_2_lower = false;
- bool is_next_lower = false;
- bool is_next_number = false;
- bool was_precedent_upper = is_ascii_upper_case(cstr[i - 1]);
- bool was_precedent_number = is_digit(cstr[i - 1]);
-
- if (i + 2 < this->size()) {
- are_next_2_lower = is_ascii_lower_case(cstr[i + 1]) && is_ascii_lower_case(cstr[i + 2]);
- }
+ bool is_curr_upper = is_ascii_upper_case(cstr[i]);
+ bool is_curr_lower = is_ascii_lower_case(cstr[i]);
+ bool is_curr_digit = is_digit(cstr[i]);
+ bool is_next_lower = false;
if (i + 1 < this->size()) {
is_next_lower = is_ascii_lower_case(cstr[i + 1]);
- is_next_number = is_digit(cstr[i + 1]);
}
- const bool cond_a = is_upper && !was_precedent_upper && !was_precedent_number;
- const bool cond_b = was_precedent_upper && is_upper && are_next_2_lower;
- const bool cond_c = is_number && !was_precedent_number;
- const bool can_break_number_letter = is_number && !was_precedent_number && is_next_lower;
- const bool can_break_letter_number = !is_number && was_precedent_number && (is_next_lower || is_next_number);
+ const bool cond_a = is_prev_lower && is_curr_upper; // aA
+ const bool cond_b = (is_prev_upper || is_prev_digit) && is_curr_upper && is_next_lower; // AAa, 2Aa
+ const bool cond_c = is_prev_digit && is_curr_lower && is_next_lower; // 2aa
+ const bool cond_d = (is_prev_upper || is_prev_lower) && is_curr_digit; // A2, a2
- bool should_split = cond_a || cond_b || cond_c || can_break_number_letter || can_break_letter_number;
- if (should_split) {
+ if (cond_a || cond_b || cond_c || cond_d) {
new_string += this->substr(start_index, i - start_index) + "_";
start_index = i;
}
}
new_string += this->substr(start_index, this->size() - start_index);
- return lowercase ? new_string.to_lower() : new_string;
+ return new_string.to_lower();
+}
+
+String String::capitalize() const {
+ String aux = this->_camelcase_to_underscore().replace("_", " ").strip_edges();
+ String cap;
+ for (int i = 0; i < aux.get_slice_count(" "); i++) {
+ String slice = aux.get_slicec(' ', i);
+ if (slice.length() > 0) {
+ slice[0] = _find_upper(slice[0]);
+ if (i > 0) {
+ cap += " ";
+ }
+ cap += slice;
+ }
+ }
+
+ return cap;
+}
+
+String String::to_camel_case() const {
+ String s = this->to_pascal_case();
+ if (!s.is_empty()) {
+ s[0] = _find_lower(s[0]);
+ }
+ return s;
+}
+
+String String::to_pascal_case() const {
+ return this->capitalize().replace(" ", "");
+}
+
+String String::to_snake_case() const {
+ return this->_camelcase_to_underscore().replace(" ", "_").strip_edges();
}
String String::get_with_code_lines() const {
diff --git a/core/string/ustring.h b/core/string/ustring.h
index 2463fc35f7..31de7cc464 100644
--- a/core/string/ustring.h
+++ b/core/string/ustring.h
@@ -196,6 +196,7 @@ class String {
bool _base_is_subsequence_of(const String &p_string, bool case_insensitive) const;
int _count(const String &p_string, int p_from, int p_to, bool p_case_insensitive) const;
+ String _camelcase_to_underscore() const;
public:
enum {
@@ -335,7 +336,9 @@ public:
static double to_float(const char32_t *p_str, const char32_t **r_end = nullptr);
String capitalize() const;
- String camelcase_to_underscore(bool lowercase = true) const;
+ String to_camel_case() const;
+ String to_pascal_case() const;
+ String to_snake_case() const;
String get_with_code_lines() const;
int get_slice_count(String p_splitter) const;
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index 8e73c8dfc0..8af2a09111 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -1506,6 +1506,9 @@ static void _register_variant_builtin_methods() {
bind_method(String, repeat, sarray("count"), varray());
bind_method(String, insert, sarray("position", "what"), varray());
bind_method(String, capitalize, sarray(), varray());
+ bind_method(String, to_camel_case, sarray(), varray());
+ bind_method(String, to_pascal_case, sarray(), varray());
+ bind_method(String, to_snake_case, sarray(), varray());
bind_method(String, split, sarray("delimiter", "allow_empty", "maxsplit"), varray(true, 0));
bind_method(String, rsplit, sarray("delimiter", "allow_empty", "maxsplit"), varray(true, 0));
bind_method(String, split_floats, sarray("delimiter", "allow_empty"), varray(true));
diff --git a/doc/classes/String.xml b/doc/classes/String.xml
index c111528c06..316bb923b7 100644
--- a/doc/classes/String.xml
+++ b/doc/classes/String.xml
@@ -775,6 +775,12 @@
Converts the String (which is a character array) to ASCII/Latin-1 encoded [PackedByteArray] (which is an array of bytes). The conversion is faster compared to [method to_utf8_buffer], as this method assumes that all the characters in the String are ASCII/Latin-1 characters, unsupported characters are replaced with spaces.
</description>
</method>
+ <method name="to_camel_case" qualifiers="const">
+ <return type="String" />
+ <description>
+ Returns the string converted to [code]camelCase[/code].
+ </description>
+ </method>
<method name="to_float" qualifiers="const">
<return type="float" />
<description>
@@ -804,6 +810,18 @@
Returns the string converted to lowercase.
</description>
</method>
+ <method name="to_pascal_case" qualifiers="const">
+ <return type="String" />
+ <description>
+ Returns the string converted to [code]PascalCase[/code].
+ </description>
+ </method>
+ <method name="to_snake_case" qualifiers="const">
+ <return type="String" />
+ <description>
+ Returns the string converted to [code]snake_case[/code].
+ </description>
+ </method>
<method name="to_upper" qualifiers="const">
<return type="String" />
<description>
diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp
index 587c16c229..4a477cd0a1 100644
--- a/editor/connections_dialog.cpp
+++ b/editor/connections_dialog.cpp
@@ -753,22 +753,12 @@ void ConnectionsDock::_open_connection_dialog(TreeItem &p_item) {
}
Dictionary subst;
-
- String s = node_name.capitalize().replace(" ", "");
- subst["NodeName"] = s;
- if (!s.is_empty()) {
- s[0] = s.to_lower()[0];
- }
- subst["nodeName"] = s;
- subst["node_name"] = node_name.capitalize().replace(" ", "_").to_lower();
-
- s = signal_name.capitalize().replace(" ", "");
- subst["SignalName"] = s;
- if (!s.is_empty()) {
- s[0] = s.to_lower()[0];
- }
- subst["signalName"] = s;
- subst["signal_name"] = signal_name.capitalize().replace(" ", "_").to_lower();
+ subst["NodeName"] = node_name.to_pascal_case();
+ subst["nodeName"] = node_name.to_camel_case();
+ subst["node_name"] = node_name.to_snake_case();
+ subst["SignalName"] = signal_name.to_pascal_case();
+ subst["signalName"] = signal_name.to_camel_case();
+ subst["signal_name"] = signal_name.to_snake_case();
String dst_method = String(EDITOR_GET("interface/editors/default_signal_callback_name")).format(subst);
diff --git a/editor/editor_autoload_settings.cpp b/editor/editor_autoload_settings.cpp
index 6d44654617..544b6c7141 100644
--- a/editor/editor_autoload_settings.cpp
+++ b/editor/editor_autoload_settings.cpp
@@ -164,7 +164,7 @@ void EditorAutoloadSettings::_autoload_add() {
if (!fpath.ends_with("/")) {
fpath = fpath.get_base_dir();
}
- dialog->config("Node", fpath.path_join(vformat("%s.gd", autoload_add_name->get_text().camelcase_to_underscore())), false, false);
+ dialog->config("Node", fpath.path_join(vformat("%s.gd", autoload_add_name->get_text().to_snake_case())), false, false);
dialog->popup_centered();
} else {
if (autoload_add(autoload_add_name->get_text(), autoload_add_path->get_text())) {
@@ -371,7 +371,7 @@ void EditorAutoloadSettings::_autoload_open(const String &fpath) {
void EditorAutoloadSettings::_autoload_file_callback(const String &p_path) {
// Convert the file name to PascalCase, which is the convention for classes in GDScript.
- const String class_name = p_path.get_file().get_basename().capitalize().replace(" ", "");
+ const String class_name = p_path.get_file().get_basename().to_pascal_case();
// If the name collides with a built-in class, prefix the name to make it possible to add without having to edit the name.
// The prefix is subjective, but it provides better UX than leaving the Add button disabled :)
@@ -580,7 +580,7 @@ void EditorAutoloadSettings::_script_created(Ref<Script> p_script) {
FileSystemDock::get_singleton()->get_script_create_dialog()->hide();
path = p_script->get_path().get_base_dir();
autoload_add_path->set_text(p_script->get_path());
- autoload_add_name->set_text(p_script->get_path().get_file().get_basename().capitalize().replace(" ", ""));
+ autoload_add_name->set_text(p_script->get_path().get_file().get_basename().to_pascal_case());
_autoload_add();
}
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 0b0c0a953a..e84fc77206 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -1314,7 +1314,7 @@ void EditorNode::save_resource_as(const Ref<Resource> &p_resource, const String
file->set_current_file(p_resource->get_path().get_file());
} else {
if (extensions.size()) {
- String resource_name_snake_case = p_resource->get_class().camelcase_to_underscore();
+ String resource_name_snake_case = p_resource->get_class().to_snake_case();
file->set_current_file("new_" + resource_name_snake_case + "." + preferred.front()->get().to_lower());
} else {
file->set_current_file(String());
@@ -1331,7 +1331,7 @@ void EditorNode::save_resource_as(const Ref<Resource> &p_resource, const String
} else if (preferred.size()) {
String existing;
if (extensions.size()) {
- String resource_name_snake_case = p_resource->get_class().camelcase_to_underscore();
+ String resource_name_snake_case = p_resource->get_class().to_snake_case();
existing = "new_" + resource_name_snake_case + "." + preferred.front()->get().to_lower();
}
file->set_current_path(existing);
@@ -2730,10 +2730,10 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
// Use casing of the root node.
break;
case SCENE_NAME_CASING_PASCAL_CASE: {
- root_name = root_name.capitalize().replace(" ", "");
+ root_name = root_name.to_pascal_case();
} break;
case SCENE_NAME_CASING_SNAKE_CASE:
- root_name = root_name.capitalize().replace(" ", "").replace("-", "_").camelcase_to_underscore();
+ root_name = root_name.replace("-", "_").to_snake_case();
break;
}
file->set_current_path(root_name + "." + extensions.front()->get().to_lower());
diff --git a/editor/plugins/bone_map_editor_plugin.cpp b/editor/plugins/bone_map_editor_plugin.cpp
index c16dca00a3..988f9cc394 100644
--- a/editor/plugins/bone_map_editor_plugin.cpp
+++ b/editor/plugins/bone_map_editor_plugin.cpp
@@ -609,7 +609,7 @@ int BoneMapper::search_bone_by_name(Skeleton3D *p_skeleton, Vector<String> p_pic
}
BoneMapper::BoneSegregation BoneMapper::guess_bone_segregation(String p_bone_name) {
- String fixed_bn = p_bone_name.camelcase_to_underscore().to_lower();
+ String fixed_bn = p_bone_name.to_snake_case();
LocalVector<String> left_words;
left_words.push_back("(?<![a-zA-Z])left");
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index 85e53011d6..8bb9452938 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -1590,7 +1590,7 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data
}
}
- String variable_name = String(node->get_name()).camelcase_to_underscore(true).validate_identifier();
+ String variable_name = String(node->get_name()).to_snake_case().validate_identifier();
if (use_type) {
text_to_drop += vformat("@onready var %s: %s = %s%s\n", variable_name, node->get_class_name(), is_unique ? "%" : "$", path);
} else {
diff --git a/editor/rename_dialog.cpp b/editor/rename_dialog.cpp
index 7b4df696b7..300a3d0f05 100644
--- a/editor/rename_dialog.cpp
+++ b/editor/rename_dialog.cpp
@@ -500,7 +500,7 @@ String RenameDialog::_postprocess(const String &subject) {
if (style_id == 1) {
// PascalCase to snake_case
- result = result.camelcase_to_underscore(true);
+ result = result.to_snake_case();
result = _regex("_+", result, "_");
} else if (style_id == 2) {
@@ -521,7 +521,7 @@ String RenameDialog::_postprocess(const String &subject) {
end = start + 1;
}
buffer += result.substr(end, result.size() - (end + 1));
- result = buffer.replace("_", "").capitalize();
+ result = buffer.to_pascal_case();
}
}
diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp
index e09862263c..7d065b4920 100644
--- a/editor/script_create_dialog.cpp
+++ b/editor/script_create_dialog.cpp
@@ -898,7 +898,7 @@ ScriptLanguage::ScriptTemplate ScriptCreateDialog::_parse_template(const ScriptL
// Get name from file name if no name in meta information
if (script_template.name == String()) {
- script_template.name = p_filename.get_basename().replace("_", " ").capitalize();
+ script_template.name = p_filename.get_basename().capitalize();
}
return script_template;
diff --git a/editor/shader_create_dialog.cpp b/editor/shader_create_dialog.cpp
index 8c4a231e8a..522fd7c645 100644
--- a/editor/shader_create_dialog.cpp
+++ b/editor/shader_create_dialog.cpp
@@ -161,7 +161,7 @@ void ShaderCreateDialog::_create_new() {
shader = text_shader;
StringBuilder code;
- code += vformat("shader_type %s;\n", mode_menu->get_text().replace(" ", "").camelcase_to_underscore());
+ code += vformat("shader_type %s;\n", mode_menu->get_text().to_snake_case());
if (current_template == 0) { // Default template.
code += "\n";
diff --git a/editor/translations/extract.py b/editor/translations/extract.py
index 7f3da400e7..07026baee2 100755
--- a/editor/translations/extract.py
+++ b/editor/translations/extract.py
@@ -139,7 +139,7 @@ theme_property_patterns = {
}
-# See String::camelcase_to_underscore().
+# See String::_camelcase_to_underscore().
capitalize_re = re.compile(r"(?<=\D)(?=\d)|(?<=\d)(?=\D([a-z]|\d))")
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
index 94dda5a74b..e84bba1179 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
@@ -436,6 +436,15 @@ namespace Godot.NativeInterop
public static partial void godotsharp_string_simplify_path(in godot_string p_self,
out godot_string r_simplified_path);
+ public static partial void godotsharp_string_to_camel_case(in godot_string p_self,
+ out godot_string r_camel_case);
+
+ public static partial void godotsharp_string_to_pascal_case(in godot_string p_self,
+ out godot_string r_pascal_case);
+
+ public static partial void godotsharp_string_to_snake_case(in godot_string p_self,
+ out godot_string r_snake_case);
+
// NodePath
public static partial void godotsharp_node_path_get_as_property_path(in godot_node_path p_self,
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
index f0bc5949df..44f951e314 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
@@ -287,6 +287,45 @@ namespace Godot
return cap;
}
+ /// <summary>
+ /// Returns the string converted to <c>camelCase</c>.
+ /// </summary>
+ /// <param name="instance">The string to convert.</param>
+ /// <returns>The converted string.</returns>
+ public static string ToCamelCase(this string instance)
+ {
+ using godot_string instanceStr = Marshaling.ConvertStringToNative(instance);
+ NativeFuncs.godotsharp_string_to_camel_case(instanceStr, out godot_string camelCase);
+ using (camelCase)
+ return Marshaling.ConvertStringToManaged(camelCase);
+ }
+
+ /// <summary>
+ /// Returns the string converted to <c>PascalCase</c>.
+ /// </summary>
+ /// <param name="instance">The string to convert.</param>
+ /// <returns>The converted string.</returns>
+ public static string ToPascalCase(this string instance)
+ {
+ using godot_string instanceStr = Marshaling.ConvertStringToNative(instance);
+ NativeFuncs.godotsharp_string_to_pascal_case(instanceStr, out godot_string pascalCase);
+ using (pascalCase)
+ return Marshaling.ConvertStringToManaged(pascalCase);
+ }
+
+ /// <summary>
+ /// Returns the string converted to <c>snake_case</c>.
+ /// </summary>
+ /// <param name="instance">The string to convert.</param>
+ /// <returns>The converted string.</returns>
+ public static string ToSnakeCase(this string instance)
+ {
+ using godot_string instanceStr = Marshaling.ConvertStringToNative(instance);
+ NativeFuncs.godotsharp_string_to_snake_case(instanceStr, out godot_string snakeCase);
+ using (snakeCase)
+ return Marshaling.ConvertStringToManaged(snakeCase);
+ }
+
private static string CamelcaseToUnderscore(this string instance, bool lowerCase)
{
string newString = string.Empty;
diff --git a/modules/mono/glue/runtime_interop.cpp b/modules/mono/glue/runtime_interop.cpp
index 0d68cb54b9..529bda4c38 100644
--- a/modules/mono/glue/runtime_interop.cpp
+++ b/modules/mono/glue/runtime_interop.cpp
@@ -1096,6 +1096,18 @@ void godotsharp_string_simplify_path(const String *p_self, String *r_simplified_
memnew_placement(r_simplified_path, String(p_self->simplify_path()));
}
+void godotsharp_string_to_camel_case(const String *p_self, String *r_camel_case) {
+ memnew_placement(r_camel_case, String(p_self->to_camel_case()));
+}
+
+void godotsharp_string_to_pascal_case(const String *p_self, String *r_pascal_case) {
+ memnew_placement(r_pascal_case, String(p_self->to_pascal_case()));
+}
+
+void godotsharp_string_to_snake_case(const String *p_self, String *r_snake_case) {
+ memnew_placement(r_snake_case, String(p_self->to_snake_case()));
+}
+
void godotsharp_node_path_get_as_property_path(const NodePath *p_ptr, NodePath *r_dest) {
memnew_placement(r_dest, NodePath(p_ptr->get_as_property_path()));
}
@@ -1471,6 +1483,9 @@ static const void *unmanaged_callbacks[]{
(void *)godotsharp_string_sha256_buffer,
(void *)godotsharp_string_sha256_text,
(void *)godotsharp_string_simplify_path,
+ (void *)godotsharp_string_to_camel_case,
+ (void *)godotsharp_string_to_pascal_case,
+ (void *)godotsharp_string_to_snake_case,
(void *)godotsharp_node_path_get_as_property_path,
(void *)godotsharp_node_path_get_concatenated_names,
(void *)godotsharp_node_path_get_concatenated_subnames,
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index cc3d14e5be..3564a6432d 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -951,14 +951,11 @@ String Node::validate_child_name(Node *p_child) {
String Node::adjust_name_casing(const String &p_name) {
switch (GLOBAL_GET("editor/node_naming/name_casing").operator int()) {
case NAME_CASING_PASCAL_CASE:
- return p_name.capitalize().replace(" ", "");
- case NAME_CASING_CAMEL_CASE: {
- String name = p_name.capitalize().replace(" ", "");
- name[0] = name.to_lower()[0];
- return name;
- }
+ return p_name.to_pascal_case();
+ case NAME_CASING_CAMEL_CASE:
+ return p_name.to_camel_case();
case NAME_CASING_SNAKE_CASE:
- return p_name.capitalize().replace(" ", "_").to_lower();
+ return p_name.to_snake_case();
}
return p_name;
}
diff --git a/tests/core/string/test_string.h b/tests/core/string/test_string.h
index 2cd837bcc9..d97da05c04 100644
--- a/tests/core/string/test_string.h
+++ b/tests/core/string/test_string.h
@@ -466,11 +466,6 @@ TEST_CASE("[String] String to float") {
}
}
-TEST_CASE("[String] CamelCase to underscore") {
- CHECK(String("TestTestStringGD").camelcase_to_underscore(false) == String("Test_Test_String_GD"));
- CHECK(String("TestTestStringGD").camelcase_to_underscore(true) == String("test_test_string_gd"));
-}
-
TEST_CASE("[String] Slicing") {
String s = "Mars,Jupiter,Saturn,Uranus";
@@ -1096,8 +1091,36 @@ TEST_CASE("[String] IPVX address to string") {
}
TEST_CASE("[String] Capitalize against many strings") {
- String input = "bytes2var";
- String output = "Bytes 2 Var";
+ String input = "2D";
+ String output = "2d";
+ CHECK(input.capitalize() == output);
+
+ input = "2d";
+ output = "2d";
+ CHECK(input.capitalize() == output);
+
+ input = "2db";
+ output = "2 Db";
+ CHECK(input.capitalize() == output);
+
+ input = "HTML5 Html5 html5 html_5";
+ output = "Html 5 Html 5 Html 5 Html 5";
+ CHECK(input.capitalize() == output);
+
+ input = "Node2D Node2d NODE2D NODE_2D node_2d";
+ output = "Node 2d Node 2d Node 2d Node 2d Node 2d";
+ CHECK(input.capitalize() == output);
+
+ input = "Node2DPosition";
+ output = "Node 2d Position";
+ CHECK(input.capitalize() == output);
+
+ input = "Number2Digits";
+ output = "Number 2 Digits";
+ CHECK(input.capitalize() == output);
+
+ input = "bytes2var";
+ output = "Bytes 2 Var";
CHECK(input.capitalize() == output);
input = "linear2db";
@@ -1112,10 +1135,6 @@ TEST_CASE("[String] Capitalize against many strings") {
output = "Sha 256";
CHECK(input.capitalize() == output);
- input = "2db";
- output = "2 Db";
- CHECK(input.capitalize() == output);
-
input = "PascalCase";
output = "Pascal Case";
CHECK(input.capitalize() == output);
@@ -1153,6 +1172,50 @@ TEST_CASE("[String] Capitalize against many strings") {
CHECK(input.capitalize() == output);
}
+struct StringCasesTestCase {
+ const char *input;
+ const char *camel_case;
+ const char *pascal_case;
+ const char *snake_case;
+};
+
+TEST_CASE("[String] Checking case conversion methods") {
+ StringCasesTestCase test_cases[] = {
+ /* clang-format off */
+ { "2D", "2d", "2d", "2d" },
+ { "2d", "2d", "2d", "2d" },
+ { "2db", "2Db", "2Db", "2_db" },
+ { "Vector3", "vector3", "Vector3", "vector_3" },
+ { "sha256", "sha256", "Sha256", "sha_256" },
+ { "Node2D", "node2d", "Node2d", "node_2d" },
+ { "RichTextLabel", "richTextLabel", "RichTextLabel", "rich_text_label" },
+ { "HTML5", "html5", "Html5", "html_5" },
+ { "Node2DPosition", "node2dPosition", "Node2dPosition", "node_2d_position" },
+ { "Number2Digits", "number2Digits", "Number2Digits", "number_2_digits" },
+ { "get_property_list", "getPropertyList", "GetPropertyList", "get_property_list" },
+ { "get_camera_2d", "getCamera2d", "GetCamera2d", "get_camera_2d" },
+ { "_physics_process", "physicsProcess", "PhysicsProcess", "_physics_process" },
+ { "bytes2var", "bytes2Var", "Bytes2Var", "bytes_2_var" },
+ { "linear2db", "linear2Db", "Linear2Db", "linear_2_db" },
+ { "sha256sum", "sha256Sum", "Sha256Sum", "sha_256_sum" },
+ { "camelCase", "camelCase", "CamelCase", "camel_case" },
+ { "PascalCase", "pascalCase", "PascalCase", "pascal_case" },
+ { "snake_case", "snakeCase", "SnakeCase", "snake_case" },
+ { "Test TEST test", "testTestTest", "TestTestTest", "test_test_test" },
+ { nullptr, nullptr, nullptr, nullptr },
+ /* clang-format on */
+ };
+
+ int idx = 0;
+ while (test_cases[idx].input != nullptr) {
+ String input = test_cases[idx].input;
+ CHECK(input.to_camel_case() == test_cases[idx].camel_case);
+ CHECK(input.to_pascal_case() == test_cases[idx].pascal_case);
+ CHECK(input.to_snake_case() == test_cases[idx].snake_case);
+ idx++;
+ }
+}
+
TEST_CASE("[String] Checking string is empty when it should be") {
bool state = true;
bool success;
@@ -1663,7 +1726,7 @@ TEST_CASE("[String] Variant ptr indexed set") {
TEST_CASE("[Stress][String] Empty via ' == String()'") {
for (int i = 0; i < 100000; ++i) {
String str = "Hello World!";
- if (str.is_empty()) {
+ if (str == String()) {
continue;
}
}