diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp index 33bf7ab48a0..a2c738b6ee2 100644 --- a/core/object/script_language.cpp +++ b/core/object/script_language.cpp @@ -633,6 +633,7 @@ void ScriptLanguage::_bind_methods() { BIND_ENUM_CONSTANT(SCRIPT_NAME_CASING_PASCAL_CASE); BIND_ENUM_CONSTANT(SCRIPT_NAME_CASING_SNAKE_CASE); BIND_ENUM_CONSTANT(SCRIPT_NAME_CASING_KEBAB_CASE); + BIND_ENUM_CONSTANT(SCRIPT_NAME_CASING_CAMEL_CASE); } bool PlaceHolderScriptInstance::set(const StringName &p_name, const Variant &p_value) { diff --git a/core/object/script_language.h b/core/object/script_language.h index 7d439da6cac..c724177f01a 100644 --- a/core/object/script_language.h +++ b/core/object/script_language.h @@ -246,6 +246,7 @@ public: SCRIPT_NAME_CASING_PASCAL_CASE, SCRIPT_NAME_CASING_SNAKE_CASE, SCRIPT_NAME_CASING_KEBAB_CASE, + SCRIPT_NAME_CASING_CAMEL_CASE, }; struct ScriptTemplate { diff --git a/core/string/char_utils.h b/core/string/char_utils.h index 9c632e52520..f0499761d70 100644 --- a/core/string/char_utils.h +++ b/core/string/char_utils.h @@ -135,3 +135,7 @@ constexpr bool is_punct(char32_t p_char) { constexpr bool is_underscore(char32_t p_char) { return (p_char == '_'); } + +constexpr bool is_hyphen(char32_t p_char) { + return (p_char == '-') || (p_char == 0x2010) || (p_char == 0x2011); +} diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index 210389a0aa1..c2f6c3b45c0 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -882,15 +882,15 @@ const char32_t *String::get_data() const { return size() ? &operator[](0) : &zero; } -String String::_camelcase_to_underscore() const { - const char32_t *cstr = get_data(); - String new_string; - int start_index = 0; - +String String::_separate_compound_words() const { if (length() == 0) { return *this; } + const char32_t *cstr = get_data(); + int start_index = 0; + String new_string; + bool is_prev_upper = is_unicode_upper_case(cstr[0]); bool is_prev_lower = is_unicode_lower_case(cstr[0]); bool is_prev_digit = is_digit(cstr[0]); @@ -911,7 +911,7 @@ String String::_camelcase_to_underscore() const { const bool cond_d = (is_prev_upper || is_prev_lower) && is_curr_digit; // A2, a2 if (cond_a || cond_b || cond_c || cond_d) { - new_string += substr(start_index, i - start_index) + "_"; + new_string += substr(start_index, i - start_index) + " "; start_index = i; } @@ -921,40 +921,72 @@ String String::_camelcase_to_underscore() const { } new_string += substr(start_index, size() - start_index); + + for (int i = 0; i < new_string.size(); i++) { + const bool whitespace = is_whitespace(new_string[i]); + const bool underscore = is_underscore(new_string[i]); + const bool hyphen = is_hyphen(new_string[i]); + + if (whitespace || underscore || hyphen) { + new_string[i] = ' '; + } + } + return new_string.to_lower(); } String String::capitalize() const { - String aux = _camelcase_to_underscore().replace_char('_', ' ').strip_edges(); - String cap; - for (int i = 0; i < aux.get_slice_count(" "); i++) { - String slice = aux.get_slicec(' ', i); + String words = _separate_compound_words().strip_edges(); + String ret; + for (int i = 0; i < words.get_slice_count(" "); i++) { + String slice = words.get_slicec(' ', i); if (slice.length() > 0) { slice[0] = _find_upper(slice[0]); if (i > 0) { - cap += " "; + ret += " "; } - cap += slice; + ret += slice; } } - - return cap; + return ret; } String String::to_camel_case() const { - String s = to_pascal_case(); - if (!s.is_empty()) { - s[0] = _find_lower(s[0]); + String words = _separate_compound_words().strip_edges(); + String ret; + for (int i = 0; i < words.get_slice_count(" "); i++) { + String slice = words.get_slicec(' ', i); + if (slice.length() > 0) { + if (i == 0) { + slice[0] = _find_lower(slice[0]); + } else { + slice[0] = _find_upper(slice[0]); + } + ret += slice; + } } - return s; + return ret; } String String::to_pascal_case() const { - return capitalize().remove_char(' '); + String words = _separate_compound_words().strip_edges(); + String ret; + for (int i = 0; i < words.get_slice_count(" "); i++) { + String slice = words.get_slicec(' ', i); + if (slice.length() > 0) { + slice[0] = _find_upper(slice[0]); + ret += slice; + } + } + return ret; } String String::to_snake_case() const { - return _camelcase_to_underscore().replace_char(' ', '_').strip_edges(); + return _separate_compound_words().replace_char(' ', '_'); +} + +String String::to_kebab_case() const { + return _separate_compound_words().replace_char(' ', '-'); } String String::get_with_code_lines() const { diff --git a/core/string/ustring.h b/core/string/ustring.h index 149c1b267f6..46dea6574d5 100644 --- a/core/string/ustring.h +++ b/core/string/ustring.h @@ -279,7 +279,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; int _count(const char *p_string, int p_from, int p_to, bool p_case_insensitive) const; - String _camelcase_to_underscore() const; + String _separate_compound_words() const; public: enum { @@ -454,6 +454,7 @@ public: String to_camel_case() const; String to_pascal_case() const; String to_snake_case() const; + String to_kebab_case() const; String get_with_code_lines() const; int get_slice_count(const String &p_splitter) const; diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index d875939413c..56fa620fd0e 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -1751,6 +1751,7 @@ static void _register_variant_builtin_methods_string() { bind_string_method(to_camel_case, sarray(), varray()); bind_string_method(to_pascal_case, sarray(), varray()); bind_string_method(to_snake_case, sarray(), varray()); + bind_string_method(to_kebab_case, sarray(), varray()); bind_string_methodv(split, static_cast (String::*)(const String &, bool, int) const>(&String::split), sarray("delimiter", "allow_empty", "maxsplit"), varray("", true, 0)); bind_string_methodv(rsplit, static_cast (String::*)(const String &, bool, int) const>(&String::rsplit), sarray("delimiter", "allow_empty", "maxsplit"), varray("", true, 0)); bind_string_method(split_floats, sarray("delimiter", "allow_empty"), varray(true)); diff --git a/doc/classes/ScriptLanguage.xml b/doc/classes/ScriptLanguage.xml index 0a1b801f9b7..30ee98e67a0 100644 --- a/doc/classes/ScriptLanguage.xml +++ b/doc/classes/ScriptLanguage.xml @@ -15,5 +15,7 @@ + + diff --git a/doc/classes/String.xml b/doc/classes/String.xml index be86f854bd3..4e0752e392b 100644 --- a/doc/classes/String.xml +++ b/doc/classes/String.xml @@ -1040,6 +1040,25 @@ [/codeblock] + + + + Returns the string converted to [code]kebab-case[/code]. + [b]Note:[/b] Numbers followed by a [i]single[/i] letter are not separated in the conversion to keep some words (such as "2D") together. + [codeblocks] + [gdscript] + "Node2D".to_kebab_case() # Returns "node-2d" + "2nd place".to_kebab_case() # Returns "2-nd-place" + "Texture3DAssetFolder".to_kebab_case() # Returns "texture-3d-asset-folder" + [/gdscript] + [csharp] + "Node2D".ToKebabCase(); // Returns "node-2d" + "2nd place".ToKebabCase(); // Returns "2-nd-place" + "Texture3DAssetFolder".ToKebabCase(); // Returns "texture-3d-asset-folder" + [/csharp] + [/codeblocks] + + diff --git a/doc/classes/StringName.xml b/doc/classes/StringName.xml index a0c68320fc9..ae3ad94c862 100644 --- a/doc/classes/StringName.xml +++ b/doc/classes/StringName.xml @@ -948,6 +948,25 @@ [/codeblock] + + + + Returns the string converted to [code]kebab-case[/code]. + [b]Note:[/b] Numbers followed by a [i]single[/i] letter are not separated in the conversion to keep some words (such as "2D") together. + [codeblocks] + [gdscript] + "Node2D".to_kebab_case() # Returns "node-2d" + "2nd place".to_kebab_case() # Returns "2-nd-place" + "Texture3DAssetFolder".to_kebab_case() # Returns "texture-3d-asset-folder" + [/gdscript] + [csharp] + "Node2D".ToKebabCase(); // Returns "node-2d" + "2nd place".ToKebabCase(); // Returns "2-nd-place" + "Texture3DAssetFolder".ToKebabCase(); // Returns "texture-3d-asset-folder" + [/csharp] + [/codeblocks] + + diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp index b787667d821..66c1c573e0e 100644 --- a/editor/connections_dialog.cpp +++ b/editor/connections_dialog.cpp @@ -255,10 +255,12 @@ StringName ConnectDialog::generate_method_callback_name(Node *p_source, const St subst["NodeName"] = node_name.to_pascal_case(); subst["nodeName"] = node_name.to_camel_case(); subst["node_name"] = node_name.to_snake_case(); + subst["node-name"] = node_name.to_kebab_case(); subst["SignalName"] = p_signal_name.to_pascal_case(); subst["signalName"] = p_signal_name.to_camel_case(); subst["signal_name"] = p_signal_name.to_snake_case(); + subst["signal-name"] = p_signal_name.to_kebab_case(); String dst_method; if (p_source == p_target) { diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index e02b2f83542..afc5fb2ef48 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -3374,11 +3374,13 @@ String EditorNode::adjust_scene_name_casing(const String &p_root_name) { // Use casing of the root node. break; case SCENE_NAME_CASING_PASCAL_CASE: - return p_root_name.replace_char('-', '_').to_pascal_case(); + return p_root_name.to_pascal_case(); case SCENE_NAME_CASING_SNAKE_CASE: - return p_root_name.replace_char('-', '_').to_snake_case(); + return p_root_name.to_snake_case(); case SCENE_NAME_CASING_KEBAB_CASE: - return p_root_name.to_snake_case().replace_char('_', '-'); + return p_root_name.to_kebab_case(); + case SCENE_NAME_CASING_CAMEL_CASE: + return p_root_name.to_camel_case(); } return p_root_name; } @@ -3395,11 +3397,13 @@ String EditorNode::adjust_script_name_casing(const String &p_file_name, ScriptLa // Script language has no preference, so do not adjust. break; case ScriptLanguage::SCRIPT_NAME_CASING_PASCAL_CASE: - return p_file_name.replace_char('-', '_').to_pascal_case(); + return p_file_name.to_pascal_case(); case ScriptLanguage::SCRIPT_NAME_CASING_SNAKE_CASE: - return p_file_name.replace_char('-', '_').to_snake_case(); + return p_file_name.to_snake_case(); case ScriptLanguage::SCRIPT_NAME_CASING_KEBAB_CASE: - return p_file_name.to_snake_case().replace_char('_', '-'); + return p_file_name.to_kebab_case(); + case ScriptLanguage::SCRIPT_NAME_CASING_CAMEL_CASE: + return p_file_name.to_camel_case(); } return p_file_name; } diff --git a/editor/editor_node.h b/editor/editor_node.h index aff8ee11334..bcd21bb9621 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -117,6 +117,7 @@ public: SCENE_NAME_CASING_PASCAL_CASE, SCENE_NAME_CASING_SNAKE_CASE, SCENE_NAME_CASING_KEBAB_CASE, + SCENE_NAME_CASING_CAMEL_CASE, }; enum ActionOnPlay { diff --git a/editor/project_manager/project_dialog.cpp b/editor/project_manager/project_dialog.cpp index c42fa5b8e30..77b7f9c3d05 100644 --- a/editor/project_manager/project_dialog.cpp +++ b/editor/project_manager/project_dialog.cpp @@ -277,7 +277,7 @@ void ProjectDialog::_update_target_auto_dir() { case 0: // No convention break; case 1: // kebab-case - new_auto_dir = new_auto_dir.to_lower().replace_char(' ', '-'); + new_auto_dir = new_auto_dir.to_kebab_case(); break; case 2: // snake_case new_auto_dir = new_auto_dir.to_snake_case(); diff --git a/editor/register_editor_types.cpp b/editor/register_editor_types.cpp index 2edba72e79e..80551de8067 100644 --- a/editor/register_editor_types.cpp +++ b/editor/register_editor_types.cpp @@ -279,8 +279,8 @@ void register_editor_types() { GLOBAL_DEF("editor/naming/default_signal_callback_name", "_on_{node_name}_{signal_name}"); GLOBAL_DEF("editor/naming/default_signal_callback_to_self_name", "_on_{signal_name}"); - GLOBAL_DEF(PropertyInfo(Variant::INT, "editor/naming/scene_name_casing", PROPERTY_HINT_ENUM, "Auto,PascalCase,snake_case,kebab-case"), EditorNode::SCENE_NAME_CASING_SNAKE_CASE); - GLOBAL_DEF(PropertyInfo(Variant::INT, "editor/naming/script_name_casing", PROPERTY_HINT_ENUM, "Auto,PascalCase,snake_case,kebab-case"), ScriptLanguage::SCRIPT_NAME_CASING_AUTO); + GLOBAL_DEF(PropertyInfo(Variant::INT, "editor/naming/scene_name_casing", PROPERTY_HINT_ENUM, "Auto,PascalCase,snake_case,kebab-case,camelCase"), EditorNode::SCENE_NAME_CASING_SNAKE_CASE); + GLOBAL_DEF(PropertyInfo(Variant::INT, "editor/naming/script_name_casing", PROPERTY_HINT_ENUM, "Auto,PascalCase,snake_case,kebab-case,camelCase"), ScriptLanguage::SCRIPT_NAME_CASING_AUTO); GLOBAL_DEF("editor/import/reimport_missing_imported_files", true); GLOBAL_DEF("editor/import/use_multiple_threads", true); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs index eb6fbdda77a..d42d101a11a 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs @@ -503,6 +503,9 @@ namespace Godot.NativeInterop public static partial void godotsharp_string_simplify_path(scoped in godot_string p_self, out godot_string r_simplified_path); + public static partial void godotsharp_string_capitalize(scoped in godot_string p_self, + out godot_string r_capitalized); + public static partial void godotsharp_string_to_camel_case(scoped in godot_string p_self, out godot_string r_camel_case); @@ -512,6 +515,9 @@ namespace Godot.NativeInterop public static partial void godotsharp_string_to_snake_case(scoped in godot_string p_self, out godot_string r_snake_case); + public static partial void godotsharp_string_to_kebab_case(scoped in godot_string p_self, + out godot_string r_kebab_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 0ca2307e3db..d1a829fe82e 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs @@ -314,22 +314,10 @@ namespace Godot /// The capitalized string. public static string Capitalize(this string instance) { - string aux = instance.CamelcaseToUnderscore(true).Replace("_", " ", StringComparison.Ordinal).Trim(); - string cap = string.Empty; - - for (int i = 0; i < aux.GetSliceCount(" "); i++) - { - string slice = aux.GetSliceCharacter(' ', i); - if (slice.Length > 0) - { - slice = char.ToUpperInvariant(slice[0]) + slice.Substring(1); - if (i > 0) - cap += " "; - cap += slice; - } - } - - return cap; + using godot_string instanceStr = Marshaling.ConvertStringToNative(instance); + NativeFuncs.godotsharp_string_capitalize(instanceStr, out godot_string capitalized); + using (capitalized) + return Marshaling.ConvertStringToManaged(capitalized); } /// @@ -371,49 +359,17 @@ namespace Godot return Marshaling.ConvertStringToManaged(snakeCase); } - private static string CamelcaseToUnderscore(this string instance, bool lowerCase) + /// + /// Returns the string converted to kebab-case. + /// + /// The string to convert. + /// The converted string. + public static string ToKebabCase(this string instance) { - string newString = string.Empty; - int startIndex = 0; - - for (int i = 1; i < instance.Length; i++) - { - bool isUpper = char.IsUpper(instance[i]); - bool isNumber = char.IsDigit(instance[i]); - - bool areNext2Lower = false; - bool isNextLower = false; - bool isNextNumber = false; - bool wasPrecedentUpper = char.IsUpper(instance[i - 1]); - bool wasPrecedentNumber = char.IsDigit(instance[i - 1]); - - if (i + 2 < instance.Length) - { - areNext2Lower = char.IsLower(instance[i + 1]) && char.IsLower(instance[i + 2]); - } - - if (i + 1 < instance.Length) - { - isNextLower = char.IsLower(instance[i + 1]); - isNextNumber = char.IsDigit(instance[i + 1]); - } - - bool condA = isUpper && !wasPrecedentUpper && !wasPrecedentNumber; - bool condB = wasPrecedentUpper && isUpper && areNext2Lower; - bool condC = isNumber && !wasPrecedentNumber; - bool canBreakNumberLetter = isNumber && !wasPrecedentNumber && isNextLower; - bool canBreakLetterNumber = !isNumber && wasPrecedentNumber && (isNextLower || isNextNumber); - - bool shouldSplit = condA || condB || condC || canBreakNumberLetter || canBreakLetterNumber; - if (shouldSplit) - { - newString += string.Concat(instance.AsSpan(startIndex, i - startIndex), "_"); - startIndex = i; - } - } - - newString += instance.Substring(startIndex, instance.Length - startIndex); - return lowerCase ? newString.ToLowerInvariant() : newString; + using godot_string instanceStr = Marshaling.ConvertStringToNative(instance); + NativeFuncs.godotsharp_string_to_kebab_case(instanceStr, out godot_string kebabCase); + using (kebabCase) + return Marshaling.ConvertStringToManaged(kebabCase); } /// diff --git a/modules/mono/glue/runtime_interop.cpp b/modules/mono/glue/runtime_interop.cpp index 7779ef299ad..abe174306eb 100644 --- a/modules/mono/glue/runtime_interop.cpp +++ b/modules/mono/glue/runtime_interop.cpp @@ -1289,6 +1289,10 @@ 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_capitalize(const String *p_self, String *r_capitalized) { + memnew_placement(r_capitalized, String(p_self->capitalize())); +} + 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())); } @@ -1301,6 +1305,10 @@ 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_string_to_kebab_case(const String *p_self, String *r_kebab_case) { + memnew_placement(r_kebab_case, String(p_self->to_kebab_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())); } @@ -1700,9 +1708,11 @@ static const void *unmanaged_callbacks[]{ (void *)godotsharp_dictionary_get_typed_value_script, (void *)godotsharp_dictionary_to_string, (void *)godotsharp_string_simplify_path, + (void *)godotsharp_string_capitalize, (void *)godotsharp_string_to_camel_case, (void *)godotsharp_string_to_pascal_case, (void *)godotsharp_string_to_snake_case, + (void *)godotsharp_string_to_kebab_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 d00c2c89565..3dd1fb511ca 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -1641,6 +1641,8 @@ String Node::adjust_name_casing(const String &p_name) { return p_name.to_camel_case(); case NAME_CASING_SNAKE_CASE: return p_name.to_snake_case(); + case NAME_CASING_KEBAB_CASE: + return p_name.to_kebab_case(); } return p_name; } @@ -3830,7 +3832,7 @@ RID Node::get_accessibility_element() const { void Node::_bind_methods() { GLOBAL_DEF(PropertyInfo(Variant::INT, "editor/naming/node_name_num_separator", PROPERTY_HINT_ENUM, "None,Space,Underscore,Dash"), 0); - GLOBAL_DEF(PropertyInfo(Variant::INT, "editor/naming/node_name_casing", PROPERTY_HINT_ENUM, "PascalCase,camelCase,snake_case"), NAME_CASING_PASCAL_CASE); + GLOBAL_DEF(PropertyInfo(Variant::INT, "editor/naming/node_name_casing", PROPERTY_HINT_ENUM, "PascalCase,camelCase,snake_case,kebab-case"), NAME_CASING_PASCAL_CASE); ClassDB::bind_static_method("Node", D_METHOD("print_orphan_nodes"), &Node::print_orphan_nodes); ClassDB::bind_method(D_METHOD("add_sibling", "sibling", "force_readable_name"), &Node::add_sibling, DEFVAL(false)); diff --git a/scene/main/node.h b/scene/main/node.h index daff975331c..e7a8f046680 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -106,7 +106,8 @@ public: enum NameCasing { NAME_CASING_PASCAL_CASE, NAME_CASING_CAMEL_CASE, - NAME_CASING_SNAKE_CASE + NAME_CASING_SNAKE_CASE, + NAME_CASING_KEBAB_CASE, }; enum InternalMode { diff --git a/tests/core/string/test_string.h b/tests/core/string/test_string.h index 1b83ff731c6..1d650768bc2 100644 --- a/tests/core/string/test_string.h +++ b/tests/core/string/test_string.h @@ -1451,6 +1451,14 @@ TEST_CASE("[String] Capitalize against many strings") { output = "Snake Snake Case"; CHECK(input.capitalize() == output); + input = "kebab-case"; + output = "Kebab Case"; + CHECK(input.capitalize() == output); + + input = "kebab-kebab-case"; + output = "Kebab Kebab Case"; + CHECK(input.capitalize() == output); + input = "sha256sum"; output = "Sha 256 Sum"; CHECK(input.capitalize() == output); @@ -1471,6 +1479,14 @@ TEST_CASE("[String] Capitalize against many strings") { output = "Snake Case Function( Snake Case Arg )"; CHECK(input.capitalize() == output); + input = "kebab-case-function( kebab-case-arg )"; + output = "Kebab Case Function( Kebab Case Arg )"; + CHECK(input.capitalize() == output); + + input = "kebab_case_function( kebab_case_arg )"; + output = "Kebab Case Function( Kebab Case Arg )"; + CHECK(input.capitalize() == output); + input = U"словоСлово_слово слово"; output = U"Слово Слово Слово Слово"; CHECK(input.capitalize() == output); @@ -1489,35 +1505,37 @@ struct StringCasesTestCase { const char32_t *camel_case; const char32_t *pascal_case; const char32_t *snake_case; + const char32_t *kebab_case; }; TEST_CASE("[String] Checking case conversion methods") { StringCasesTestCase test_cases[] = { /* clang-format off */ - { U"2D", U"2d", U"2d", U"2d" }, - { U"2d", U"2d", U"2d", U"2d" }, - { U"2db", U"2Db", U"2Db", U"2_db" }, - { U"Vector3", U"vector3", U"Vector3", U"vector_3" }, - { U"sha256", U"sha256", U"Sha256", U"sha_256" }, - { U"Node2D", U"node2d", U"Node2d", U"node_2d" }, - { U"RichTextLabel", U"richTextLabel", U"RichTextLabel", U"rich_text_label" }, - { U"HTML5", U"html5", U"Html5", U"html_5" }, - { U"Node2DPosition", U"node2dPosition", U"Node2dPosition", U"node_2d_position" }, - { U"Number2Digits", U"number2Digits", U"Number2Digits", U"number_2_digits" }, - { U"get_property_list", U"getPropertyList", U"GetPropertyList", U"get_property_list" }, - { U"get_camera_2d", U"getCamera2d", U"GetCamera2d", U"get_camera_2d" }, - { U"_physics_process", U"physicsProcess", U"PhysicsProcess", U"_physics_process" }, - { U"bytes2var", U"bytes2Var", U"Bytes2Var", U"bytes_2_var" }, - { U"linear2db", U"linear2Db", U"Linear2Db", U"linear_2_db" }, - { U"sha256sum", U"sha256Sum", U"Sha256Sum", U"sha_256_sum" }, - { U"camelCase", U"camelCase", U"CamelCase", U"camel_case" }, - { U"PascalCase", U"pascalCase", U"PascalCase", U"pascal_case" }, - { U"snake_case", U"snakeCase", U"SnakeCase", U"snake_case" }, - { U"Test TEST test", U"testTestTest", U"TestTestTest", U"test_test_test" }, - { U"словоСлово_слово слово", U"словоСловоСловоСлово", U"СловоСловоСловоСлово", U"слово_слово_слово_слово" }, - { U"λέξηΛέξη_λέξη λέξη", U"λέξηΛέξηΛέξηΛέξη", U"ΛέξηΛέξηΛέξηΛέξη", U"λέξη_λέξη_λέξη_λέξη" }, - { U"բառԲառ_բառ բառ", U"բառԲառԲառԲառ", U"ԲառԲառԲառԲառ", U"բառ_բառ_բառ_բառ" }, - { nullptr, nullptr, nullptr, nullptr }, + { U"2D", U"2d", U"2d", U"2d", U"2d" }, + { U"2d", U"2d", U"2d", U"2d", U"2d" }, + { U"2db", U"2Db", U"2Db", U"2_db", U"2-db" }, + { U"Vector3", U"vector3", U"Vector3", U"vector_3", U"vector-3" }, + { U"sha256", U"sha256", U"Sha256", U"sha_256", U"sha-256" }, + { U"Node2D", U"node2d", U"Node2d", U"node_2d", U"node-2d" }, + { U"RichTextLabel", U"richTextLabel", U"RichTextLabel", U"rich_text_label", U"rich-text-label" }, + { U"HTML5", U"html5", U"Html5", U"html_5", U"html-5" }, + { U"Node2DPosition", U"node2dPosition", U"Node2dPosition", U"node_2d_position", U"node-2d-position" }, + { U"Number2Digits", U"number2Digits", U"Number2Digits", U"number_2_digits", U"number-2-digits" }, + { U"get_property_list", U"getPropertyList", U"GetPropertyList", U"get_property_list", U"get-property-list" }, + { U"get_camera_2d", U"getCamera2d", U"GetCamera2d", U"get_camera_2d", U"get-camera-2d" }, + { U"_physics_process", U"physicsProcess", U"PhysicsProcess", U"_physics_process", U"-physics-process" }, + { U"bytes2var", U"bytes2Var", U"Bytes2Var", U"bytes_2_var", U"bytes-2-var" }, + { U"linear2db", U"linear2Db", U"Linear2Db", U"linear_2_db", U"linear-2-db" }, + { U"sha256sum", U"sha256Sum", U"Sha256Sum", U"sha_256_sum", U"sha-256-sum" }, + { U"camelCase", U"camelCase", U"CamelCase", U"camel_case", U"camel-case" }, + { U"PascalCase", U"pascalCase", U"PascalCase", U"pascal_case", U"pascal-case" }, + { U"snake_case", U"snakeCase", U"SnakeCase", U"snake_case", U"snake-case" }, + { U"kebab-case", U"kebabCase", U"KebabCase", U"kebab_case", U"kebab-case" }, + { U"Test TEST test", U"testTestTest", U"TestTestTest", U"test_test_test", U"test-test-test" }, + { U"словоСлово_слово слово", U"словоСловоСловоСлово", U"СловоСловоСловоСлово", U"слово_слово_слово_слово", U"слово-слово-слово-слово" }, + { U"λέξηΛέξη_λέξη λέξη", U"λέξηΛέξηΛέξηΛέξη", U"ΛέξηΛέξηΛέξηΛέξη", U"λέξη_λέξη_λέξη_λέξη", U"λέξη-λέξη-λέξη-λέξη" }, + { U"բառԲառ_բառ բառ", U"բառԲառԲառԲառ", U"ԲառԲառԲառԲառ", U"բառ_բառ_բառ_բառ", U"բառ-բառ-բառ-բառ" }, + { nullptr, nullptr, nullptr, nullptr, nullptr }, /* clang-format on */ }; @@ -1527,6 +1545,7 @@ TEST_CASE("[String] Checking case conversion methods") { 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); + CHECK(input.to_kebab_case() == test_cases[idx].kebab_case); idx++; } }