Packages
io_ansi_table
0.3.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 the properties of configured table styles.
# """
@moduledoc false
use PersistConfig
alias IO.ANSI.Table.{Column, LineType}
@type attr :: atom | [atom]
@type border :: String.t()
@type t :: atom
@styles Application.get_env(@app, :table_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` to a table style.
## 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` to 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 style |> inspect() |> String.length()
arg = style |> to_string() |> 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)
@doc """
Returns the list of all table styles.
## Examples
iex> alias IO.ANSI.Table.Style
iex> Style.styles() |> length()
36
"""
@spec styles() :: [t]
def styles(), do: Enum.sort(@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, LineType.to_line_types(@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]
"""
@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
"""
@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)
:light_cyan
iex> alias IO.ANSI.Table.Style
iex> Style.key_attr(:light, :header)
[:light_yellow, :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" |> String.to_atom()
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 (one per table style)
and optionally passes each one to a function.
## Examples
alias IO.ANSI.Table.Style
Style.texts(" - `&style`&filler - ¬e\n")
alias IO.ANSI.Table.Style
Style.texts(" - `&style`&filler - ¬e", &IO.puts/1)
## 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() -> any)) :: [any]
def texts(template, fun \\ & &1) when is_function(fun, 1) do
@styles
|> Enum.sort(&(elem(&1, 0) <= elem(&2, 0)))
|> Stream.map(&interpolate(&1, template))
|> Enum.map(&fun.(&1))
end
## Private functions
@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