Skip to content

Commit 3be3afb

Browse files
authored
Add String.titlecase/2 (#10008)
1 parent 199acd1 commit 3be3afb

File tree

2 files changed

+75
-2
lines changed

2 files changed

+75
-2
lines changed

lib/elixir/lib/string.ex

+40-2
Original file line numberDiff line numberDiff line change
@@ -859,15 +859,53 @@ defmodule String do
859859
def capitalize(string, mode \\ :default)
860860

861861
def capitalize(<<char, rest::binary>>, :ascii) do
862-
char = if char >= ?a and char <= ?z, do: char - 32, else: char
863-
<<char>> <> downcase(rest, :ascii)
862+
<<capitalize_ascii_char(char)>> <> downcase(rest, :ascii)
864863
end
865864

866865
def capitalize(string, mode) when is_binary(string) do
867866
{char, rest} = String.Casing.titlecase_once(string, mode)
868867
char <> downcase(rest, mode)
869868
end
870869

870+
defp capitalize_ascii_char(char) when char >= ?a and char <= ?z, do: char - 32
871+
defp capitalize_ascii_char(char), do: char
872+
873+
@doc """
874+
Converts only the first character in the given string to
875+
uppercase according to `mode`.
876+
877+
`mode` may be `:default`, `:ascii` or `:greek`. The `:default` mode considers
878+
all non-conditional transformations outlined in the Unicode standard. `:ascii`
879+
lowercases only the letters A to Z. `:greek` includes the context sensitive
880+
mappings found in Greek.
881+
882+
## Examples
883+
884+
iex> String.titlecase("abcd")
885+
"Abcd"
886+
887+
iex> String.titlecase("ABCd")
888+
"ABCd"
889+
890+
iex> String.titlecase("fin")
891+
"Fin"
892+
893+
iex> String.titlecase("olá")
894+
"Olá"
895+
896+
"""
897+
@spec titlecase(t, :default | :ascii | :greek) :: t
898+
def titlecase(string, mode \\ :default)
899+
900+
def titlecase(<<char, rest::binary>>, :ascii) do
901+
<<capitalize_ascii_char(char)>> <> rest
902+
end
903+
904+
def titlecase(string, mode) when is_binary(string) do
905+
{char, rest} = String.Casing.titlecase_once(string, mode)
906+
char <> rest
907+
end
908+
871909
@doc false
872910
@deprecated "Use String.trim_trailing/1 instead"
873911
defdelegate rstrip(binary), to: String.Break, as: :trim_trailing

lib/elixir/test/elixir/string_test.exs

+35
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,41 @@ defmodule StringTest do
210210
assert String.capitalize("aáA", :ascii) == "Aáa"
211211
end
212212

213+
test "titlecase/1" do
214+
assert String.titlecase("") == ""
215+
assert String.titlecase("abc") == "Abc"
216+
assert String.titlecase("ABC") == "ABC"
217+
assert String.titlecase("c b a") == "C b a"
218+
assert String.titlecase("1ABc") == "1ABc"
219+
assert String.titlecase("_aBc1") == "_aBc1"
220+
assert String.titlecase(" aBc1") == " aBc1"
221+
end
222+
223+
test "titlecase/1 with UTF-8" do
224+
assert String.titlecase("àáâ") == "Àáâ"
225+
assert String.titlecase("ÀÁÂ") == "ÀÁÂ"
226+
assert String.titlecase("âáàÕ") == "ÂáàÕ"
227+
assert String.titlecase("ÂÁÀõ") == "ÂÁÀõ"
228+
assert String.titlecase("òóôõö") == "Òóôõö"
229+
assert String.titlecase("ÒÓÔÕÖ") == "ÒÓÔÕÖ"
230+
assert String.titlecase("fin") == "Fin"
231+
end
232+
233+
test "titlecase/2 with ascii" do
234+
assert String.titlecase("àáâ", :ascii) == "àáâ"
235+
assert String.titlecase("aáA", :ascii) == "AáA"
236+
end
237+
238+
test "titlecase/1 with greek final sigma" do
239+
assert String.titlecase("σ") == "Σ"
240+
assert String.titlecase("μΜσΣ") == "ΜΜσΣ"
241+
end
242+
243+
test "titlecase/2 with greek final sigma" do
244+
assert String.titlecase("σ", :greek) == "Σ"
245+
assert String.titlecase("σ ςΠ", :greek) == "Σ ςΠ"
246+
end
247+
213248
test "replace_leading/3" do
214249
assert String.replace_leading("aa abc ", "a", "b") == "bb abc "
215250
assert String.replace_leading("__ abc ", "_", "b") == "bb abc "

0 commit comments

Comments
 (0)