Renders an Elixir term with syntax-highlighted pretty-printing.
Maps and keyword lists print an atom key as :key: value and every other key as
key => value. Structs are displayed with the last segment of their module name.
The :expand option forces multi-line output with one entry per line.
Collections nested inside a collection are not descended into: they print as
.... Only nil, booleans, atoms, integers, floats, binaries, lists, keyword
lists, maps and structs can be rendered at the top level — any other term, a
tuple or a pid among them, raises FunctionClauseError from format_pretty/3.
Component tag
Tag :pretty, built by Drafter.App as {:pretty, data, opts}:
pretty(data, opts)The positional argument becomes :data directly; it is never re-read from
opts. Pass the term positionally. Writing pretty(data: term) puts a keyword
list in the positional slot, which the renderer then treats as options, leaving
:data as nil and rendering nothing.
Options
:data- the term to display. Defaultnil, which renders the textnil. Supplied positionally through thepretty/2element.:expand-boolean/0, put one entry per line. Defaultfalse.:syntax_highlighting-boolean/0, colour the tokens. Defaulttrue.:style-map/0of style overrides passed to the theme computation. Default%{}.:class- theme class atom or list of them, normalised byDrafter.Style.normalize_classes/1and reachingmount/1as:classes. Default[].:height-pos_integer/0read only bypreferred_height/2, never bymount/1. Default5.
update/2 accepts every option above. Through the component tree only :data
is live-updatable — update_props_from_mount/3 returns :data and
:app_module alone, so :expand, :syntax_highlighting, :style and
:classes are mount-only.
Usage
pretty(%{name: "Alice", age: 30, active: true})
pretty(my_struct, expand: true)
Summary
Functions
The component tag this widget registers under.
Formats a keyword list. Keys are written with a leading colon and a trailing
colon, so [a: 1] prints as [:a: 1].
Formats a plain list. Each element goes through format_simple/2, so a nested
collection prints as ....
Formats a map that is not a struct, one format_pair/3 per entry in key order.
Formats one map entry.
Formats data into the widget's marked-up text.
Formats a single value nested inside a collection.
Formats a struct, headed by the last segment of its module name.
Builds the props map for a {:pretty, data, opts} element.
Ignores every event and returns {:noreply, state}.
Builds the widget state from props.
The {r, g, b} colour for a "{token_kind}" marker.
The number of rows the element asks for: opts[:height], default 5.
Draws the formatted term into rect, one strip per line of output.
The token colour table, keyed by token kind.
Callback implementation for Drafter.Widget.unmount/1.
Replaces the state fields named in props, keeping the current value for any key
that is absent.
Narrows a re-render to :data and :app_module.
Types
Functions
@spec component_tag() :: :pretty
The component tag this widget registers under.
iex> Drafter.Widget.Pretty.component_tag()
:pretty
Formats a keyword list. Keys are written with a leading colon and a trailing
colon, so [a: 1] prints as [:a: 1].
iex> Drafter.Widget.Pretty.format_keyword([a: 1, b: 2], false, false)
"[:a: 1, :b: 2]"
iex> Drafter.Widget.Pretty.format_keyword([a: 1], false, true)
"[\n :a: 1\n]"
Formats a plain list. Each element goes through format_simple/2, so a nested
collection prints as ....
iex> Drafter.Widget.Pretty.format_list([1, 2], false, false)
"[1, 2]"
iex> Drafter.Widget.Pretty.format_list([1, 2], false, true)
"[\n 1,\n 2\n]"
Formats a map that is not a struct, one format_pair/3 per entry in key order.
iex> Drafter.Widget.Pretty.format_map(%{a: 1}, false, false)
"%{:a: 1}"
iex> Drafter.Widget.Pretty.format_map(%{a: 1}, false, true)
"%{\n :a: 1\n}"
Formats one map entry.
An atom key becomes :key: value; every other key becomes key => value.
iex> Drafter.Widget.Pretty.format_pair(:a, 1, false)
":a: 1"
iex> Drafter.Widget.Pretty.format_pair("k", 1, false)
"\"k\" => 1"
Formats data into the widget's marked-up text.
With highlight set, each token is followed by a §{kind} marker that
render/2 turns into a colour; without it the result is plain text. expand
puts one entry of a collection per line.
Handles nil, booleans, atoms, integers, floats, binaries, lists, keyword lists,
maps and structs. Any other term raises FunctionClauseError.
iex> Drafter.Widget.Pretty.format_pretty(nil, false, false)
"nil"
iex> Drafter.Widget.Pretty.format_pretty(42, true, false)
"42§{integer}"
iex> Drafter.Widget.Pretty.format_pretty("hi", false, false)
"\"hi\""
iex> Drafter.Widget.Pretty.format_pretty([1, 2, 3], false, false)
"[1, 2, 3]"
iex> Drafter.Widget.Pretty.format_pretty([a: 1, b: 2], false, false)
"[:a: 1, :b: 2]"
iex> Drafter.Widget.Pretty.format_pretty(%{a: 1}, false, false)
"%{:a: 1}"
iex> Drafter.Widget.Pretty.format_pretty(%{"k" => 1}, false, false)
"%{\"k\" => 1}"
iex> Drafter.Widget.Pretty.format_pretty([1, [2]], false, false)
"[1, ...]"
Formats a single value nested inside a collection.
Handles nil, booleans, atoms, integers, floats and binaries. Anything else,
including a nested list, map or tuple, returns "..." rather than recursing.
iex> Drafter.Widget.Pretty.format_simple(:ok, false)
":ok"
iex> Drafter.Widget.Pretty.format_simple(:ok, true)
":ok§{atom}"
iex> Drafter.Widget.Pretty.format_simple([1, 2], false)
"..."
Formats a struct, headed by the last segment of its module name.
Field names go through format_simple/2, so they carry a leading colon.
iex> Drafter.Widget.Pretty.format_struct(1..3, false, false)
"%Range{:first: 1, :last: 3, :step: 1}"
iex> Drafter.Widget.Pretty.format_struct(1..2//1, false, true)
"%Range{\n :first: 1,\n :last: 2,\n :step: 1\n}"
@spec from_component_opts( term(), keyword() ) :: Drafter.Widget.props()
Builds the props map for a {:pretty, data, opts} element.
data becomes :data as it stands and is never re-read from opts, so
pretty(data: term) leaves :data as the keyword list itself only if the
renderer passes it positionally. :class is normalised into :classes and
:__app_module__ becomes :app_module.
iex> props = Drafter.Widget.Pretty.from_component_opts(%{a: 1}, expand: true)
iex> {props.data, props.expand, props.syntax_highlighting, props.classes}
{%{a: 1}, true, true, []}
@spec handle_event(Drafter.Event.t(), t() | Drafter.Widget.props()) :: {:noreply, t()}
Ignores every event and returns {:noreply, state}.
A plain props map is passed through mount/1 first, so the returned state is
always a t/0. The widget is not focusable.
@spec mount(Drafter.Widget.props()) :: t()
Builds the widget state from props.
Reads :data (default nil), :expand (default false),
:syntax_highlighting (default true), :style (default %{}), :classes
(default []) and :app_module (default nil).
iex> state = Drafter.Widget.Pretty.mount(%{data: %{a: 1}})
iex> {state.data, state.expand, state.syntax_highlighting}
{%{a: 1}, false, true}
@spec parse_color_spec(String.t()) :: {0..255, 0..255, 0..255}
The {r, g, b} colour for a "{token_kind}" marker.
spec must start with { — anything else raises FunctionClauseError. An
unknown token kind falls back to the :default colour, {200, 200, 200}.
iex> Drafter.Widget.Pretty.parse_color_spec("{integer}")
{181, 206, 168}
iex> Drafter.Widget.Pretty.parse_color_spec("{not_a_token}")
{200, 200, 200}
@spec preferred_height( term(), keyword() ) :: pos_integer()
The number of rows the element asks for: opts[:height], default 5.
iex> Drafter.Widget.Pretty.preferred_height(nil, [])
5
iex> Drafter.Widget.Pretty.preferred_height(%{a: 1}, height: 12)
12
@spec render(t() | Drafter.Widget.props(), Drafter.Widget.rect()) :: [ Drafter.Draw.Strip.t() ]
Draws the formatted term into rect, one strip per line of output.
state may be a plain props map, in which case it is passed through mount/1
first. Each line is padded with spaces or truncated to rect.width. The number
of strips follows the formatted term, not rect.height.
@spec syntax_colors() :: %{required(atom()) => {0..255, 0..255, 0..255}}
The token colour table, keyed by token kind.
iex> Drafter.Widget.Pretty.syntax_colors() |> Map.keys() |> Enum.sort()
[:atom, :boolean, :default, :float, :integer, :keyword_key, :map_key, nil, :separator, :string, :struct_name]
iex> Drafter.Widget.Pretty.syntax_colors().string
{235, 203, 139}
Callback implementation for Drafter.Widget.unmount/1.
@spec update(Drafter.Widget.props(), t()) :: t()
Replaces the state fields named in props, keeping the current value for any key
that is absent.
Accepts :data, :expand, :syntax_highlighting, :style, :classes and
:app_module. A :data of nil in props counts as a value and clears the
term.
iex> state = Drafter.Widget.Pretty.mount(%{data: 1})
iex> updated = Drafter.Widget.Pretty.update(%{expand: true}, state)
iex> {updated.data, updated.expand}
{1, true}
@spec update_props_from_mount(Drafter.Widget.props(), term(), keyword()) :: Drafter.Widget.props()
Narrows a re-render to :data and :app_module.
:expand, :syntax_highlighting, :style and :classes are dropped, so they
are mount-only through the component tree.
iex> props = Drafter.Widget.Pretty.from_component_opts(%{a: 1}, expand: true)
iex> Drafter.Widget.Pretty.update_props_from_mount(props, %{}, [])
%{data: %{a: 1}, app_module: nil}