Add Options, Functions and Settings to convert Node-Names and Strings to kebab-case

- refactored and renamed String::_camelcase_to_underscore to String:_separate_compound_words
- refactored String::to_snake_case to work with the refactored String::_separate_compound_words
- created char_utils::is_hyphen to catch all hyphen variants in kebab-case conversion
- created String::to_kebab_case using the new String::_separate_compound_words
- created corresponding Documentation in String and StringName
- simplified both switch statements in EditorNode and ProjectDialog
- added new kebab-casing Option for Node Names in ProjectSettings
- added missing camelCase Options to Scene- and Node-Names in ProjectSettings
- simplified Mono RuntimeInterop Functions
- hooked up the ConnectionsDialog
- created additional Unit Tests
This commit is contained in:
Priahoud 2024-07-28 21:32:28 +02:00 committed by priahoud
parent 06c71fbf40
commit bf963e767e
20 changed files with 195 additions and 114 deletions

View file

@ -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) {

View file

@ -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 {

View file

@ -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);
}

View file

@ -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]);
}
return s;
ret += slice;
}
}
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 {

View file

@ -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;

View file

@ -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<Vector<String> (String::*)(const String &, bool, int) const>(&String::split), sarray("delimiter", "allow_empty", "maxsplit"), varray("", true, 0));
bind_string_methodv(rsplit, static_cast<Vector<String> (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));

View file

@ -15,5 +15,7 @@
</constant>
<constant name="SCRIPT_NAME_CASING_KEBAB_CASE" value="3" enum="ScriptNameCasing">
</constant>
<constant name="SCRIPT_NAME_CASING_CAMEL_CASE" value="4" enum="ScriptNameCasing">
</constant>
</constants>
</class>

View file

@ -1040,6 +1040,25 @@
[/codeblock]
</description>
</method>
<method name="to_kebab_case" qualifiers="const">
<return type="String" />
<description>
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]
</description>
</method>
<method name="to_lower" qualifiers="const">
<return type="String" />
<description>

View file

@ -948,6 +948,25 @@
[/codeblock]
</description>
</method>
<method name="to_kebab_case" qualifiers="const">
<return type="String" />
<description>
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]
</description>
</method>
<method name="to_lower" qualifiers="const">
<return type="String" />
<description>

View file

@ -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) {

View file

@ -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;
}

View file

@ -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 {

View file

@ -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();

View file

@ -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);

View file

@ -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,

View file

@ -314,22 +314,10 @@ namespace Godot
/// <returns>The capitalized string.</returns>
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);
}
/// <summary>
@ -371,49 +359,17 @@ namespace Godot
return Marshaling.ConvertStringToManaged(snakeCase);
}
private static string CamelcaseToUnderscore(this string instance, bool lowerCase)
/// <summary>
/// Returns the string converted to <c>kebab-case</c>.
/// </summary>
/// <param name="instance">The string to convert.</param>
/// <returns>The converted string.</returns>
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);
}
/// <summary>

View file

@ -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,

View file

@ -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));

View file

@ -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 {

View file

@ -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++;
}
}