Current section

Files

Jump to
io_ansi_plus lib io ansi plus ie.ex
Raw

lib/io/ansi/plus/ie.ex

defmodule IO.ANSI.Plus.IE do
@moduledoc false
alias IO.ANSI.Plus, as: ANSI
defmacro __using__(_options) do
quote do
import unquote(__MODULE__)
alias unquote(__MODULE__)
alias IO.ANSI.Plus, as: ANSI
:ok
end
end
@spec color_samples :: [String.t()]
def color_samples do
:io_ansi_plus
|> Application.get_env(:colors)
|> Enum.map(fn %{code: code, hex: hex, names: names} ->
"#" <> hex_val = hex
"- ![#{hex}](https://placehold.it/15/#{hex_val}/000000?text=+)" <>
"#{color_sample_names(code, names)} (#{code})"
end)
end
@spec print_color_samples :: :ok
def print_color_samples do
color_samples() |> Enum.each(&IO.puts/1)
end
@spec print_standard_colors(pos_integer, pos_integer) :: :ok
def print_standard_colors(squares_per_row, column_width) do
for row <- standard_color_rows(squares_per_row) do
Enum.each(row, &print_square(&1, column_width, _with_code = false))
IO.write("\n")
end
IO.write("\n")
end
@spec print_xterm_colors(Range.t(), pos_integer, pos_integer) :: :ok
def print_xterm_colors(%Range{} = range, squares_per_row, column_width) do
for row <- xterm_color_rows(range, squares_per_row) do
Enum.each(row, &print_square(&1, column_width, _with_code? = true))
IO.write("\n")
end
IO.write("\n")
end
@spec print_color_chart :: :ok
def print_color_chart do
IO.write("\n")
print_standard_colors(8, 12)
print_xterm_colors(0..15, 8, 12)
print_xterm_colors(16..255, 6, 18)
end
@spec names_for_code(0..255) :: [atom]
def names_for_code(code) when code in 0..255 do
names =
:io_ansi_plus
|> Application.get_env(:colors)
|> Enum.find_value(fn %{code: color_code, names: names} ->
if color_code == code, do: names
end)
if code in 0..15 do
[standard_color_names() |> Enum.at(code) | names]
else
if(Enum.empty?(names), do: [:"color#{code}"], else: names)
end
end
## Private functions
@spec fave_name_codes :: %{non_neg_integer => atom}
defp fave_name_codes do
:io_ansi_plus
|> Application.get_env(:colors)
|> Enum.reduce(%{}, fn
%{code: _code, names: []}, acc -> acc
%{code: code, names: [name | _]}, acc -> Map.put(acc, code, name)
end)
end
@spec standard_color_rows(pos_integer) :: [keyword(non_neg_integer)]
defp standard_color_rows(squares_per_row) do
standard_color_names()
|> Enum.with_index()
|> Enum.chunk_every(squares_per_row)
end
@spec standard_color_names :: [atom]
defp standard_color_names do
names = [:black, :red, :green, :yellow, :blue, :magenta, :cyan, :white]
lites = Enum.map(names, fn name -> :"light_#{name}" end)
names ++ lites
end
@spec xterm_color_rows(Range.t(), pos_integer) :: [keyword(non_neg_integer)]
defp xterm_color_rows(%Range{} = range, squares_per_row) do
codes = fave_name_codes()
for i <- range do
if codes[i], do: {codes[i], i}, else: {:"color#{i}", i}
end
|> Enum.chunk_every(squares_per_row)
end
@spec print_square({atom, non_neg_integer}, pos_integer, boolean) :: :ok
defp print_square({name, code}, column_width, with_code?) do
[
background(name),
foreground(code),
text(name, code, column_width, with_code?)
]
|> ANSI.write()
end
@spec background(atom) :: atom
defp background(name), do: :"#{name}_background"
@spec black_foreground_codes :: [non_neg_integer]
defp black_foreground_codes do
[7, 10, 11, 14, 15] ++
Enum.to_list(34..51) ++
Enum.to_list(70..87) ++
Enum.to_list(106..123) ++
Enum.to_list(142..159) ++
Enum.to_list(178..195) ++ Enum.to_list(214..231) ++ Enum.to_list(244..255)
end
defp foreground(code) do
if code in black_foreground_codes(), do: :black, else: :light_white
end
@spec adjust_text(String.t(), pos_integer) :: String.t()
defp adjust_text(text, column_width) do
if String.length(text) > column_width - 1 do
String.slice(text, 0..(column_width - 3)) <> "…"
else
text
end
|> String.pad_trailing(column_width)
end
@spec text(atom, non_neg_integer, pos_integer, boolean) :: String.t()
defp text(name, _code, column_width, _with_code? = false) do
"#{inspect(name)}" |> adjust_text(column_width)
end
defp text(name, code, column_width, _with_code? = true) do
text = "#{inspect(name)} (#{code})"
if String.length(text) > column_width - 1 do
"#{inspect(name)}"
else
text
end
|> adjust_text(column_width)
end
@spec color_sample_names(non_neg_integer, [atom]) :: String.t()
defp color_sample_names(code, _names = []) do
if code in 0..15 do
"`#{standard_color_names() |> Enum.at(code) |> inspect()}`"
else
"`:color#{code}`"
end
end
defp color_sample_names(code, names) do
names =
if code in 0..15 do
[standard_color_names() |> Enum.at(code) | names]
else
names
end
names
|> Enum.reduce("", fn name, acc -> acc <> "`#{inspect(name)}` " end)
|> String.trim_trailing()
end
end