Current section
Files
Jump to
Current section
Files
lib/title/word.ex
defmodule Title.Word do
@moduledoc false
@articles [
"a",
"an",
"the"
]
@conjunctions [
"for",
"and",
"nor",
"but",
"or",
"yet",
"so"
]
@prepositions [
"aboard",
"about",
"above",
"across",
"after",
"against",
"along",
"amid",
"among",
"anti",
"around",
"as",
"at",
"before",
"behind",
"below",
"beneath",
"beside",
"besides",
"between",
"beyond",
"but",
"by",
"concerning",
"considering",
"despite",
"down",
"during",
"except",
"excepting",
"excluding",
"following",
"for",
"from",
"in",
"inside",
"into",
"like",
"minus",
"near",
"of",
"off",
"on",
"onto",
"opposite",
"over",
"past",
"per",
"plus",
"regarding",
"round",
"save",
"since",
"than",
"through",
"to",
"toward",
"towards",
"under",
"underneath",
"unlike",
"until",
"up",
"upon",
"versus",
"via",
"with",
"within",
"without"
]
@styles %{
ap: @articles ++ @conjunctions ++ Enum.filter(@prepositions, &(String.length(&1) < 4)),
chicago: @articles ++ @conjunctions ++ @prepositions,
ny_times: @articles ++ @conjunctions ++ @prepositions,
wikipedia: @articles ++ @conjunctions ++ Enum.filter(@prepositions, &(String.length(&1) < 5))
}
def titelize([first | rest], %Title.Opts{} = opts) do
[do_capitalize(first, opts, true), titelize_next(rest, opts)]
end
defp titelize_next([], _opts), do: []
defp titelize_next([word | rest], opts) do
[do_capitalize(word, opts, rest == []), titelize_next(rest, opts)]
end
defp do_capitalize(word, _, _) when word in [" ", "-"], do: word
defp do_capitalize(word, %{style: style, custom: custom}, first_or_last) do
re_word =
~r/[a-zA-Z]+/
|> Regex.scan(word)
|> Enum.join()
|> Regex.compile!("i")
with :not_custom <- customize(word, re_word, custom),
:not_downcase <- downcase(word, re_word, style, not first_or_last),
{start, length} <- alpha_index(word) do
binary_part(word, 0, start) <>
String.capitalize(binary_part(word, start, length)) <>
String.downcase(binary_part(word, start + length, byte_size(word) - start - length))
else
word -> word
end
end
defp customize(word, re_word, custom) do
case Enum.find(custom, &Regex.match?(re_word, &1)) do
nil -> :not_custom
custom -> Regex.replace(re_word, word, custom)
end
end
defp downcase(word, re_word, style, downcase?) do
if downcase? and Enum.any?(@styles[style], &Regex.match?(re_word, &1)) do
String.downcase(word)
else
:not_downcase
end
end
defp alpha_index(word) do
case Regex.scan(~r/[a-zA-Z]+/, word, return: :index) do
[[index] | _] -> index
_ -> word
end
end
end