AK: Add String{,Utils}::to_snakecase()

This is an improved version of WrapperGenerator's snake_name(), which
seems like the kind of thing that could be useful elsewhere but would
end up getting duplicated - so let's add this to AK::String instead,
like to_{lowercase,uppercase}().
This commit is contained in:
Linus Groh 2021-02-20 22:39:22 +01:00 committed by Andreas Kling
parent 43948aee51
commit 4fafe14691
5 changed files with 50 additions and 0 deletions

View file

@ -389,6 +389,11 @@ String String::to_uppercase() const
return m_impl->to_uppercase();
}
String String::to_snakecase() const
{
return StringUtils::to_snakecase(*this);
}
bool operator<(const char* characters, const String& string)
{
if (!characters)

View file

@ -131,6 +131,7 @@ public:
String to_lowercase() const;
String to_uppercase() const;
String to_snakecase() const;
bool is_whitespace() const { return StringUtils::is_whitespace(*this); }

View file

@ -29,6 +29,7 @@
#include <AK/Memory.h>
#include <AK/Optional.h>
#include <AK/String.h>
#include <AK/StringBuilder.h>
#include <AK/StringUtils.h>
#include <AK/StringView.h>
#include <AK/Vector.h>
@ -350,6 +351,33 @@ Optional<size_t> find(const StringView& haystack, const StringView& needle)
haystack.characters_without_null_termination(), haystack.length(),
needle.characters_without_null_termination(), needle.length());
}
String to_snakecase(const StringView& str)
{
auto should_insert_underscore = [&](auto i, auto current_char) {
if (i == 0)
return false;
auto previous_ch = str[i - 1];
if (islower(previous_ch) && isupper(current_char))
return true;
if (i >= str.length() - 1)
return false;
auto next_ch = str[i + 1];
if (isupper(current_char) && islower(next_ch))
return true;
return false;
};
StringBuilder builder;
for (size_t i = 0; i < str.length(); ++i) {
auto ch = str[i];
if (should_insert_underscore(i, ch))
builder.append('_');
builder.append(tolower(ch));
}
return builder.to_string();
}
}
}

View file

@ -72,6 +72,8 @@ bool contains(const StringView&, const StringView&, CaseSensitivity);
bool is_whitespace(const StringView&);
StringView trim_whitespace(const StringView&, TrimMode mode);
Optional<size_t> find(const StringView& haystack, const StringView& needle);
String to_snakecase(const StringView&);
}
}

View file

@ -310,4 +310,18 @@ TEST_CASE(find)
EXPECT_EQ(AK::StringUtils::find(test_string, "78").has_value(), false);
}
TEST_CASE(to_snakecase)
{
EXPECT_EQ(AK::StringUtils::to_snakecase("foobar"), "foobar");
EXPECT_EQ(AK::StringUtils::to_snakecase("Foobar"), "foobar");
EXPECT_EQ(AK::StringUtils::to_snakecase("FOOBAR"), "foobar");
EXPECT_EQ(AK::StringUtils::to_snakecase("fooBar"), "foo_bar");
EXPECT_EQ(AK::StringUtils::to_snakecase("FooBar"), "foo_bar");
EXPECT_EQ(AK::StringUtils::to_snakecase("fooBAR"), "foo_bar");
EXPECT_EQ(AK::StringUtils::to_snakecase("FOOBar"), "foo_bar");
EXPECT_EQ(AK::StringUtils::to_snakecase("foo_bar"), "foo_bar");
EXPECT_EQ(AK::StringUtils::to_snakecase("FBar"), "f_bar");
EXPECT_EQ(AK::StringUtils::to_snakecase("FooB"), "foo_b");
}
TEST_MAIN(StringUtils)