Packages
io_ansi_table
1.0.21
1.0.40
1.0.39
1.0.38
1.0.37
1.0.36
1.0.34
1.0.33
1.0.32
1.0.31
1.0.30
1.0.29
1.0.28
1.0.27
1.0.26
1.0.25
1.0.24
1.0.23
1.0.22
1.0.21
1.0.20
1.0.19
1.0.18
1.0.17
1.0.16
1.0.15
1.0.14
1.0.13
1.0.12
1.0.11
1.0.10
1.0.9
1.0.8
1.0.7
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.4.32
0.4.31
0.4.30
0.4.29
0.4.28
0.4.27
0.4.26
0.4.25
0.4.24
0.4.23
0.4.22
0.4.21
0.4.20
0.4.19
0.4.18
0.4.17
0.4.16
0.4.15
0.4.14
0.4.13
0.4.12
0.4.11
0.4.10
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.29
0.3.28
0.3.27
0.3.26
0.3.25
0.3.24
0.3.23
0.3.22
0.3.21
0.3.20
0.3.19
0.3.18
0.3.17
0.3.16
0.3.15
0.3.14
0.3.13
0.3.12
0.3.11
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.20
0.1.19
0.1.18
0.1.17
0.1.16
0.1.15
0.1.14
0.1.13
0.1.12
0.1.11
0.1.10
0.1.9
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
Writes data to "stdio" in a table with borders and colors. Can choose a table style to change the look of the table.
Current section
Files
Jump to
Current section
Files
lib/io/ansi/table/style.ex
defmodule IO.ANSI.Table.Style do
@moduledoc """
Defines functions returning various aspects of the configured table styles.
"""
use PersistConfig
alias IO.ANSI.Table.{Column, LineType, LineTypes}
@typedoc "Style attribute"
@type attr :: atom | [atom]
@typedoc "Table border"
@type border :: String.t()
@typedoc "Table style"
@type t :: atom
@styles get_env(:table_styles)
# Enum.sort(@styles, fn {style1, _}, {style2, _} -> style1 <= style2 end)
@sorted_styles Enum.sort(@styles)
Module.register_attribute(__MODULE__, :inner_lengths, accumulate: true)
Module.register_attribute(__MODULE__, :left_lengths, accumulate: true)
Module.register_attribute(__MODULE__, :line_types, accumulate: true)
Module.register_attribute(__MODULE__, :right_lengths, accumulate: true)
Module.register_attribute(__MODULE__, :style_ids, accumulate: true)
Module.register_attribute(__MODULE__, :style_lengths, accumulate: true)
Module.register_attribute(__MODULE__, :types, accumulate: true)
@doc """
Converts a switch `argument` into a table style.
E.g. `green-alt` in: `no tx -blt green-alt 11`.
## Examples
iex> alias IO.ANSI.Table.Style
iex> Style.from_switch_arg("green-alt")
{:ok, :green_alt}
iex> alias IO.ANSI.Table.Style
iex> Style.from_switch_arg("lite")
:error
"""
@spec from_switch_arg(String.t()) :: {:ok, t} | :error
def from_switch_arg(arg)
@doc """
Converts a table `style` into a switch argument.
## Examples
iex> alias IO.ANSI.Table.Style
iex> Style.to_switch_arg(:green_alt)
"green-alt"
iex> alias IO.ANSI.Table.Style
iex> Style.to_switch_arg(:lite)
nil
"""
@spec to_switch_arg(t) :: String.t() | nil
def to_switch_arg(style)
for {style, _} <- @styles do
@style_ids style
@style_lengths inspect(style) |> String.length()
arg = to_string(style) |> String.replace("_", "-")
def from_switch_arg(unquote(arg)), do: {:ok, unquote(style)}
def to_switch_arg(unquote(style)), do: unquote(arg)
end
def from_switch_arg(_arg), do: :error
def to_switch_arg(_style), do: nil
@max_length Enum.max(@style_lengths)
@sorted_style_ids Enum.sort(@style_ids)
@doc """
Returns the sorted list of all style IDs.
## Examples
iex> alias IO.ANSI.Table.Style
iex> length Style.styles
36
iex> alias IO.ANSI.Table.Style
iex> Enum.take Style.styles, 7
[:bare, :barish, :cyan, :cyan_alt, :cyan_border, :cyan_mult, :dark]
"""
@spec styles :: [t]
def styles, do: @sorted_style_ids
@doc """
Returns the dash of a given table `style` and line `type`.
## Examples
iex> IO.ANSI.Table.Style
iex> Style.dash(:dark, :top)
"═"
iex> IO.ANSI.Table.Style
iex> Style.dash(:dark, :row)
nil
"""
@spec dash(t, LineType.t()) :: String.t() | nil
def dash(style, type)
@doc """
Returns the borders of a given table `style` and line `type`.
## Examples
iex> alias IO.ANSI.Table.Style
iex> Style.borders(:cyan, :bottom)
["╚═", "═╩═", "═╝"]
"""
@spec borders(t, LineType.t()) :: [border]
def borders(style, type)
@doc """
Returns the "border spreads" of a given table `style` and line `type`.
## Examples
iex> alias IO.ANSI.Table.Style
iex> Style.border_spreads(:plain, :bottom)
[[0, 2, 0], [0, 3, 0], [0, 2, 0]] # borders: "└─", "─┴─", "─┘"
iex> alias IO.ANSI.Table.Style
iex> Style.border_spreads(:plain, :header)
[[0, 1, 1], [1, 1, 1], [1, 1, 0]] # borders: "│" , "│" , "│"
"""
@spec border_spreads(t, LineType.t()) :: [Column.spread()]
def border_spreads(style, type)
@doc """
Returns the "line types" of a given table `style`.
## Examples
iex> alias IO.ANSI.Table.Style
iex> Style.line_types(:light)
[:top, :header, :separator, [:row], :bottom]
iex> alias IO.ANSI.Table.Style
iex> Style.line_types(:green_alt)
[:top, :header, :separator, [:even_row, :odd_row]]
"""
@spec line_types(t) :: [LineType.t()]
def line_types(style)
for {style, %{borders: borders}} <- @styles do
for {type, [left, inner, right, dash]} <- borders do
@left_lengths String.length(left)
@inner_lengths String.length(inner)
@right_lengths String.length(right)
@types type
def dash(unquote(style), unquote(type)), do: unquote(dash)
def borders(unquote(style), unquote(type)) do
[unquote(left), unquote(inner), unquote(right)]
end
end
@max_left_length Enum.max(@left_lengths)
@max_inner_length Enum.max(@inner_lengths)
@max_right_length Enum.max(@right_lengths)
for {type, [left, inner, right, _dash]} <- borders do
left_spread = Column.spread(@max_left_length, left, :left)
inner_spread = Column.spread(@max_inner_length, inner, :center)
right_spread = Column.spread(@max_right_length, right, :right)
def border_spreads(unquote(style), unquote(type)) do
[unquote(left_spread), unquote(inner_spread), unquote(right_spread)]
end
end
@line_types {style, LineTypes.from(@types)}
def line_types(unquote(style)), do: @line_types[unquote(style)]
Module.delete_attribute(__MODULE__, :left_lengths)
Module.delete_attribute(__MODULE__, :inner_lengths)
Module.delete_attribute(__MODULE__, :right_lengths)
Module.delete_attribute(__MODULE__, :types)
end
def dash(_style, _type), do: nil
def borders(_style, _type), do: []
def border_spreads(_style, _type), do: []
def line_types(_style), do: []
@doc """
Returns the "border attribute" of a given table `style` and line `type`.
## Examples
iex> alias IO.ANSI.Table.Style
iex> Style.border_attr(:green, :top)
[:light_white, :green_background]
iex> alias IO.ANSI.Table.Style
iex> Style.border_attr(:cyan_alt, :odd_row)
[:chartreuse_yellow, :chartreuse_yellow_background]
"""
@spec border_attr(t, LineType.t()) :: attr | nil
def border_attr(style, type)
@doc """
Returns the "filler attribute" of a given table `style` and line `type`.
## Examples
iex> alias IO.ANSI.Table.Style
iex> Style.filler_attr(:mixed, :row)
:green_background
iex> alias IO.ANSI.Table.Style
iex> Style.filler_attr(:cyan_alt, :odd_row)
:chartreuse_yellow_background
"""
@spec filler_attr(t, LineType.t()) :: attr | nil
def filler_attr(style, type)
@doc """
Returns the "key attribute" of a given table `style` and line `type`.
## Examples
iex> alias IO.ANSI.Table.Style
iex> Style.key_attr(:light, :row)
:chartreuse
iex> alias IO.ANSI.Table.Style
iex> Style.key_attr(:light, :header)
[:gold, :underline]
"""
@spec key_attr(t, LineType.t()) :: attr | nil
def key_attr(style, type)
@doc """
Returns the "non-key attribute" of a given table `style` and line `type`.
## Examples
iex> alias IO.ANSI.Table.Style
iex> Style.non_key_attr(:cyan, :row)
[:black, :cyan_background]
"""
@spec non_key_attr(t, LineType.t()) :: attr | nil
def non_key_attr(style, type)
@attr_funs [:border_attr, :filler_attr, :key_attr, :non_key_attr]
for fun <- @attr_funs do
key = :"#{fun}s"
for {style, style_map} <- @styles do
attrs = Map.get(style_map, key)
if Keyword.keyword?(attrs) do
for {type, attr} <- attrs do
def unquote(fun)(unquote(style), unquote(type)), do: unquote(attr)
end
else
for type <- List.flatten(@line_types[style]) do
def unquote(fun)(unquote(style), unquote(type)), do: unquote(attrs)
end
end
end
def unquote(fun)(_style, _type), do: nil
end
@doc ~S"""
Returns a list of interpolated texts (for all styles) based on `template`.
## Examples
iex> alias IO.ANSI.Table.Style
iex> Style.texts(" - `&style`&filler - ¬e\n") |> Enum.slice(16..18)
[" - `:green_border` - light green border\n",
" - `:green_border_padded` - light green border with extra padding\n",
" - `:green_border_unpadded` - light green border without padding\n"]
## Interpolation placeholders
- `&style` - table style (e.g. ":light_alt")
- `&arg` - table style switch arg (e.g. "light-alt")
- `&filler` - padding after &style or &arg
- `¬e` - table style note
- `&rank` - table style rank (3 digits)
"""
@spec texts(String.t()) :: [String.t()]
def texts(template), do: Enum.map(@sorted_styles, &interpolate(&1, template))
@doc """
Lists all the table styles alphabetically.
"""
@spec table_styles :: Keyword.t()
def table_styles, do: @sorted_styles
## Private functions
# @doc ~S'''
# Replaces interpolation placeholders of a table `style` in `template`.
# ## Examples
# iex> alias IO.ANSI.Table.Style
# iex> template = "• (&rank) &style&filler ➜ ¬e"
# iex> sty1 = :green_border_unpadded
# iex> sty2 = :light
# iex> sty3 = :medium
# iex> {Style.interpolate({sty1, Style.table_styles[sty1]}, template),
# ...> Style.interpolate({sty2, Style.table_styles[sty2]}, template),
# ...> Style.interpolate({sty3, Style.table_styles[sty3]}, template)}
# {"• (210) :green_border_unpadded ➜ light green border without padding",
# "• (010) :light ➜ light colors",
# "• (020) :medium ➜ medium colors"}
# iex> alias IO.ANSI.Table.Style
# iex> template = "• (&rank) &style&filler ➜ ¬e\n"
# iex> sty1 = :green_border_unpadded
# iex> sty2 = :light
# iex> sty3 = :medium
# iex> sty1_map = Style.table_styles[sty1] |> put_in([:note], "")
# iex> sty2_map = Style.table_styles[sty2] |> put_in([:note], "")
# iex> sty3_map = Style.table_styles[sty3] |> put_in([:note], "")
# iex> {Style.interpolate({sty1, sty1_map}, template),
# ...> Style.interpolate({sty2, sty2_map}, template),
# ...> Style.interpolate({sty3, sty3_map}, template)}
# {"• (210) :green_border_unpadded\n",
# "• (010) :light\n",
# "• (020) :medium\n"}
# '''
@spec interpolate({t, map}, String.t()) :: String.t()
defp interpolate({style, %{note: note, rank: rank}}, template) do
import String, only: [duplicate: 2, replace: 3, slice: 2]
{style, arg} = {inspect(style), to_switch_arg(style)}
filler = duplicate(" ", @max_length - String.length(style))
# Ensure rank always 3 digits...
# Erase for example trailing " - "...
# Erase trailing " () "...
template
|> replace("&style", style)
|> replace("&arg", arg)
|> replace("&filler", filler)
|> replace("¬e", note)
|> replace("&rank", slice("0#{rank}", -3..-1))
|> replace(~r/ +. *$/u, "")
|> replace(~r/ +\(\) *$/, "")
end
end