Packages
io_ansi_table
0.4.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/column.ex
defmodule IO.ANSI.Table.Column do
@moduledoc """
Functions related to the columns of a table.
"""
use PersistConfig
alias IO.ANSI.Plus, as: ANSI
alias IO.ANSI.Table.{Line, Row, Spec}
@type align_attr :: :left | :center | :right
@type align_spec :: any | {align_attr, any}
@type attr :: align_attr | sort_attr
@type sort_attr :: MapSorter.SortSpec.sort_dir()
@type sort_spec :: MapSorter.SortSpec.t()
@type sort_symbol :: {sort_attr, String.t()} | {:pos, atom | [atom]}
@type spec :: align_spec | sort_spec
@type spread :: [width]
@type t :: [String.t()]
@type width :: non_neg_integer
@ansi_escape_char "\e"
@ansi_escape_codes ~r/\e\[([0-9]{1,3}(;[0-9]{1,3})*)?[m|K]/
@max_width_range Application.get_env(@app, :max_width_range)
@upper_max_width @max_width_range.last
@spec left_margin(Spec.t()) :: Spec.t()
def left_margin(spec) do
margin =
case spec.margins[:left] do
# Move the cursor forward N columns: \e[<N>C
n when is_integer(n) and n >= 1 ->
ANSI.cursor_right(n)
_ ->
""
end
%{spec | left_margin: margin}
end
@spec align_attrs(Spec.t()) :: Spec.t()
def align_attrs(spec) do
attrs = Enum.map(spec.headers, &find_attr(&1, spec.align_specs, :left))
%{spec | align_attrs: attrs}
end
@spec sort_attrs(Spec.t()) :: Spec.t()
def sort_attrs(spec) do
attrs = Enum.map(spec.headers, &find_attr(&1, spec.sort_specs, :asc))
%{spec | sort_attrs: attrs}
end
@doc """
Returns the `specs` attribute of a `header`.
## Examples
iex> alias IO.ANSI.Table.Column
iex> sort_specs = ["dept", desc: "hired"]
iex> {
...> Column.find_attr("dept" , sort_specs, :asc),
...> Column.find_attr("hired", sort_specs, :asc),
...> Column.find_attr("job" , sort_specs, :asc)
...> }
{:asc, :desc, nil}
"""
@spec find_attr(any, [spec], attr) :: any | attr
def find_attr(header, specs, default_attr) do
Enum.find_value(specs, fn
{attr, key} when key == header -> attr
key when key == header -> default_attr
_ -> false
end)
end
@spec column_widths(Spec.t()) :: Spec.t()
def column_widths(spec) do
widths =
[spec.headings | spec.rows]
|> transpose()
|> widths(spec.max_width)
%{spec | column_widths: widths}
end
@doc """
Returns a list of column widths capped by `maximum width`.
## Examples
iex> alias IO.ANSI.Table.Column
iex> data = [["cat", "wombat", "elk"], ["mongoose", "ant", "gnu"]]
iex> Column.widths(data)
[6, 8]
iex> alias IO.ANSI.Table.Column
iex> data = [["cat", "wombat", "elk"], ["mongoose", "ant", "gnu"]]
iex> Column.widths(data, 7)
[6, 7]
iex> alias IO.ANSI.Table.Column
iex> data = [["\e[32m\e[42mCHEETAH\e[0m", "elk"], ["mongoose", "ant"]]
iex> Column.widths(data)
[7, 8]
"""
@spec widths([t], width) :: [width]
def widths(columns, max_width \\ @upper_max_width) do
for column <- columns do
column |> Enum.map(&column_width/1) |> Enum.max() |> min(max_width)
end
end
@doc """
Spreads a `width` given an `element` and its `align attribute`.
## Examples
iex> alias IO.ANSI.Table.Column
iex> {
...> Column.spread(7, "name", :left ),
...> Column.spread(7, "name", :center),
...> Column.spread(7, "name", :right )
...> }
{[0, 4, 3], [1, 4, 2], [3, 4, 0]}
iex> alias IO.ANSI.Table.Column
iex> {
...> Column.spread(3, "name", :left ),
...> Column.spread(3, "name", :center),
...> Column.spread(3, "name", :right )
...> }
{[0, 3, 0], [0, 3, 0], [0, 3, 0]}
iex> alias IO.ANSI.Table.Column
iex> {
...> Column.spread(10, "\e[32m\e[42mCHEETAH\e[0m", :left ),
...> Column.spread(10, "\e[32m\e[42mCHEETAH\e[0m", :center),
...> Column.spread(10, "\e[32m\e[42mCHEETAH\e[0m", :right )
...> }
{[0, 21, 3], [1, 21, 2], [3, 21, 0]}
iex> alias IO.ANSI.Table.Column
iex> {
...> Column.spread(7, "\e[32m\e[42mCHEETAH\e[0m", :left ),
...> Column.spread(7, "\e[32m\e[42mCHEETAH\e[0m", :center),
...> Column.spread(7, "\e[32m\e[42mCHEETAH\e[0m", :right )
...> }
{[0, 21, 0], [0, 21, 0], [0, 21, 0]}
"""
@spec spread(width, Line.elem(), align_attr) :: spread
def spread(width, elem, _align_attr = :left) do
elem_width = elem |> column_width() |> min(width)
[0, io_width(elem, elem_width), width - elem_width]
end
def spread(width, elem, _align_attr = :right) do
elem_width = elem |> column_width() |> min(width)
[width - elem_width, io_width(elem, elem_width), 0]
end
def spread(width, elem, _align_attr = :center) do
elem_width = elem |> column_width() |> min(width)
left_width = div(width - elem_width, 2)
right_width = width - left_width - elem_width
[left_width, io_width(elem, elem_width), right_width]
end
def spread(width, elem, _align_attr), do: spread(width, elem, :left)
## Private functions
@spec column_width(String.t()) :: non_neg_integer
defp column_width(@ansi_escape_char <> _rest = elem) do
elem |> String.replace(@ansi_escape_codes, "") |> String.length()
end
defp column_width(elem), do: String.length(elem)
@spec io_width(String.t(), non_neg_integer) :: non_neg_integer
defp io_width(@ansi_escape_char <> _rest = elem, _elem_width) do
String.length(elem)
end
defp io_width(_elem, elem_width), do: elem_width
# @doc """
# Transposes rows to columns..
# ## Examples
# iex> alias IO.ANSI.Table.Column
# iex> rows = [
# ...> ["1", "2", "3"],
# ...> ["4", "5", "6"]
# ...> ]
# iex> Column.transpose(rows)
# [["1", "4"], ["2", "5"], ["3", "6"]]
# """
@spec transpose([Row.t()]) :: [t]
defp transpose(rows), do: rows |> Stream.zip() |> Enum.map(&Tuple.to_list/1)
end