-module(spruce@list).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/spruce/list.gleam").
-export([new/0, item/2, child/3, nested/3, kind/2, enumerator/2, render/2]).
-export_type([enumerator/0, items/0, item/0, kind/0, list_/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Pure bullet and ordered list rendering.\n"
"\n"
" Lists are rendered with an explicit `Spruce` context. Bullet lists use a\n"
" Unicode bullet when color is supported and a deterministic ASCII marker when\n"
" it is not. Ordered lists count from one at each nesting depth.\n"
).
-type enumerator() :: auto | {custom, fun((integer(), integer()) -> binary())}.
-type items() :: empty | {cons, item(), items()}.
-type item() :: {item, binary(), items()}.
-type kind() :: bullet | ordered.
-opaque list_() :: {list, items(), kind(), enumerator()}.
-file("src/spruce/list.gleam", 40).
?DOC(" Build an empty bullet list.\n").
-spec new() -> list_().
new() ->
{list, empty, bullet, auto}.
-file("src/spruce/list.gleam", 161).
-spec append_item(items(), item()) -> items().
append_item(Items, Item) ->
case Items of
empty ->
{cons, Item, empty};
{cons, First, Rest} ->
{cons, First, append_item(Rest, Item)}
end.
-file("src/spruce/list.gleam", 45).
?DOC(" Add a top-level item, preserving insertion order.\n").
-spec item(list_(), binary()) -> list_().
item(List_, Label) ->
{list,
append_item(erlang:element(2, List_), {item, Label, empty}),
erlang:element(3, List_),
erlang:element(4, List_)}.
-file("src/spruce/list.gleam", 168).
-spec labels_to_items(list(binary())) -> items().
labels_to_items(Labels) ->
case Labels of
[] ->
empty;
[First | Rest] ->
{cons, {item, First, empty}, labels_to_items(Rest)}
end.
-file("src/spruce/list.gleam", 53).
?DOC(" Add a top-level item with one level of child labels.\n").
-spec child(list_(), binary(), list(binary())) -> list_().
child(List_, Label, Children) ->
{list,
append_item(
erlang:element(2, List_),
{item, Label, labels_to_items(Children)}
),
erlang:element(3, List_),
erlang:element(4, List_)}.
-file("src/spruce/list.gleam", 67).
?DOC(
" Add a top-level item whose children come from another list.\n"
"\n"
" The nested list's item tree is preserved, while the parent list's kind and\n"
" enumerator control rendering at every depth.\n"
).
-spec nested(list_(), binary(), list_()) -> list_().
nested(List_, Label, Children) ->
{list,
append_item(
erlang:element(2, List_),
{item, Label, erlang:element(2, Children)}
),
erlang:element(3, List_),
erlang:element(4, List_)}.
-file("src/spruce/list.gleam", 78).
?DOC(" Set the default marker style for the list.\n").
-spec kind(list_(), kind()) -> list_().
kind(List_, Kind_) ->
{list, erlang:element(2, List_), Kind_, erlang:element(4, List_)}.
-file("src/spruce/list.gleam", 87).
?DOC(
" Set a custom enumerator.\n"
"\n"
" The function receives the one-based item index within the current depth and\n"
" the one-based depth of the item being rendered. It should return the complete\n"
" marker to place before that item's first label line.\n"
).
-spec enumerator(list_(), fun((integer(), integer()) -> binary())) -> list_().
enumerator(List_, Enumerate) ->
{list,
erlang:element(2, List_),
erlang:element(3, List_),
{custom, Enumerate}}.
-file("src/spruce/list.gleam", 151).
-spec render_label(binary(), binary(), binary()) -> list(binary()).
render_label(Prefix, Follow, Label) ->
case gleam@string:split(Label, <<"\n"/utf8>>) of
[] ->
[Prefix];
[First | Rest] ->
[<<Prefix/binary, First/binary>> |
gleam@list:map(
Rest,
fun(Line) -> <<Follow/binary, Line/binary>> end
)]
end.
-file("src/spruce/list.gleam", 193).
-spec bullet_marker(spruce:spruce()) -> binary().
bullet_marker(Sp) ->
gleam@bool:guard(
not spruce:supports_color(Sp),
<<"- "/utf8>>,
fun() -> <<"• "/utf8>> end
).
-file("src/spruce/list.gleam", 176).
-spec marker(spruce:spruce(), kind(), enumerator(), integer(), integer()) -> binary().
marker(Sp, Kind, Enumerator, Index, Depth) ->
case Enumerator of
{custom, Enumerate} ->
Enumerate(Index, Depth);
auto ->
case Kind of
bullet ->
bullet_marker(Sp);
ordered ->
<<(erlang:integer_to_binary(Index))/binary, ". "/utf8>>
end
end.
-file("src/spruce/list.gleam", 124).
-spec render_item(
spruce:spruce(),
item(),
kind(),
enumerator(),
binary(),
integer(),
integer()
) -> list(binary()).
render_item(Sp, Item, Kind, Enumerator, Base, Depth, Index) ->
Prefix = <<<<Base/binary,
(gleam@string:repeat(<<" "/utf8>>, Depth - 1))/binary>>/binary,
(marker(Sp, Kind, Enumerator, Index, Depth))/binary>>,
Follow = gleam@string:repeat(
<<" "/utf8>>,
spruce@align:visual_length(Prefix)
),
_pipe = render_label(Prefix, Follow, erlang:element(2, Item)),
lists:append(
_pipe,
render_items(
Sp,
erlang:element(3, Item),
Kind,
Enumerator,
Base,
Depth + 1,
1
)
).
-file("src/spruce/list.gleam", 99).
-spec render_items(
spruce:spruce(),
items(),
kind(),
enumerator(),
binary(),
integer(),
integer()
) -> list(binary()).
render_items(Sp, Items, Kind, Enumerator, Base, Depth, Index) ->
case Items of
empty ->
[];
{cons, First, Rest} ->
_pipe = render_item(Sp, First, Kind, Enumerator, Base, Depth, Index),
lists:append(
_pipe,
render_items(Sp, Rest, Kind, Enumerator, Base, Depth, Index + 1)
)
end.
-file("src/spruce/list.gleam", 92).
?DOC(" Render a list to a string.\n").
-spec render(spruce:spruce(), list_()) -> binary().
render(Sp, List_) ->
Base = gleam@string:repeat(<<" "/utf8>>, spruce:depth(Sp)),
_pipe = render_items(
Sp,
erlang:element(2, List_),
erlang:element(3, List_),
erlang:element(4, List_),
Base,
1,
1
),
gleam@string:join(_pipe, <<"\n"/utf8>>).