-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] -> [<> | gleam@list:map( Rest, fun(Line) -> <> 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 = <<<>, 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>>).