-module(spruce@layout).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/spruce/layout.gleam").
-export([join_vertical/2, join_horizontal/2, place/5]).
-export_type([pos/0, block/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(" ANSI-aware layout helpers for composing multiline text blocks.\n").
-type pos() :: start | center | 'end'.
-type block() :: {block, integer(), integer(), list(binary())}.
-file("src/spruce/layout.gleam", 110).
-spec pad(binary(), integer(), pos()) -> binary().
pad(Text, Width, Pos) ->
case Pos of
start ->
spruce@align:pad_right(Text, Width);
center ->
spruce@align:pad_center(Text, Width);
'end' ->
spruce@align:pad_left(Text, Width)
end.
-file("src/spruce/layout.gleam", 138).
-spec max_visual_width(list(binary())) -> integer().
max_visual_width(Lines) ->
_pipe = Lines,
_pipe@1 = gleam@list:map(_pipe, fun spruce@align:visual_length/1),
gleam@list:fold(_pipe@1, 0, fun gleam@int:max/2).
-file("src/spruce/layout.gleam", 131).
-spec prepend_reversed(list(binary()), list(binary())) -> list(binary()).
prepend_reversed(Lines, Acc) ->
case Lines of
[] ->
Acc;
[Line | Rest] ->
prepend_reversed(Rest, [Line | Acc])
end.
-file("src/spruce/layout.gleam", 123).
-spec flatten_lines_loop(list(binary()), list(binary())) -> list(binary()).
flatten_lines_loop(Blocks, Acc) ->
case Blocks of
[] ->
Acc;
[Block | Rest] ->
flatten_lines_loop(
Rest,
prepend_reversed(gleam@string:split(Block, <<"\n"/utf8>>), Acc)
)
end.
-file("src/spruce/layout.gleam", 118).
-spec flatten_lines(list(binary())) -> list(binary()).
flatten_lines(Blocks) ->
_pipe = flatten_lines_loop(Blocks, []),
lists:reverse(_pipe).
-file("src/spruce/layout.gleam", 20).
?DOC(" Stack blocks vertically, padding every line to the widest visual width.\n").
-spec join_vertical(pos(), list(binary())) -> binary().
join_vertical(Pos, Blocks) ->
Lines = flatten_lines(Blocks),
Width = max_visual_width(Lines),
_pipe = Lines,
_pipe@1 = gleam@list:map(_pipe, fun(Line) -> pad(Line, Width, Pos) end),
gleam@string:join(_pipe@1, <<"\n"/utf8>>).
-file("src/spruce/layout.gleam", 183).
-spec collect_row(list(list(binary())), binary(), list(list(binary()))) -> {ok,
{binary(), list(list(binary()))}} |
{error, nil}.
collect_row(Blocks, Row, Tails) ->
case Blocks of
[] ->
{ok, {Row, lists:reverse(Tails)}};
[[] | _] ->
{error, nil};
[[Line | Rest] | Remaining] ->
collect_row(Remaining, <<Row/binary, Line/binary>>, [Rest | Tails])
end.
-file("src/spruce/layout.gleam", 173).
-spec join_normalized_blocks(list(list(binary())), list(binary())) -> binary().
join_normalized_blocks(Blocks, Rows) ->
case collect_row(Blocks, <<""/utf8>>, []) of
{error, nil} ->
_pipe = Rows,
_pipe@1 = lists:reverse(_pipe),
gleam@string:join(_pipe@1, <<"\n"/utf8>>);
{ok, {Row, Tails}} ->
join_normalized_blocks(Tails, [Row | Rows])
end.
-file("src/spruce/layout.gleam", 162).
-spec repeat_line_loop(binary(), integer(), list(binary())) -> list(binary()).
repeat_line_loop(Line, Times, Acc) ->
case Times =< 0 of
true ->
Acc;
false ->
repeat_line_loop(Line, Times - 1, [Line | Acc])
end.
-file("src/spruce/layout.gleam", 156).
-spec repeat_line(binary(), integer()) -> list(binary()).
repeat_line(Line, Times) ->
_pipe = repeat_line_loop(Line, Times, []),
lists:reverse(_pipe).
-file("src/spruce/layout.gleam", 99).
-spec padding_counts(integer(), pos()) -> {integer(), integer()}.
padding_counts(Extra, Pos) ->
case Pos of
start ->
{0, Extra};
center ->
Before = Extra div 2,
{Before, Extra - Before};
'end' ->
{Extra, 0}
end.
-file("src/spruce/layout.gleam", 77).
-spec position_lines(list(binary()), integer(), integer(), integer(), pos()) -> list(binary()).
position_lines(Lines, Content_height, Width, Target_height, Pos) ->
Extra = Target_height - Content_height,
case Extra =< 0 of
true ->
Lines;
false ->
{Before, After} = padding_counts(Extra, Pos),
Blank = gleam@string:repeat(<<" "/utf8>>, Width),
_pipe = repeat_line(Blank, Before),
_pipe@1 = lists:append(_pipe, Lines),
lists:append(_pipe@1, repeat_line(Blank, After))
end.
-file("src/spruce/layout.gleam", 70).
-spec normalize_block(block(), integer(), pos()) -> list(binary()).
normalize_block(Block, Target_height, Pos) ->
Padded_lines = gleam@list:map(
erlang:element(4, Block),
fun(Line) -> pad(Line, erlang:element(2, Block), start) end
),
position_lines(
Padded_lines,
erlang:element(3, Block),
erlang:element(2, Block),
Target_height,
Pos
).
-file("src/spruce/layout.gleam", 148).
-spec max_block_height_loop(list(block()), integer()) -> integer().
max_block_height_loop(Blocks, Acc) ->
case Blocks of
[] ->
Acc;
[{block, _, Height, _} | Rest] ->
max_block_height_loop(Rest, gleam@int:max(Acc, Height))
end.
-file("src/spruce/layout.gleam", 144).
-spec max_block_height(list(block())) -> integer().
max_block_height(Blocks) ->
max_block_height_loop(Blocks, 0).
-file("src/spruce/layout.gleam", 64).
-spec block_info(binary()) -> block().
block_info(Block) ->
{Width, _} = spruce@align:size(Block),
Height = spruce@align:height(Block),
{block, Width, Height, gleam@string:split(Block, <<"\n"/utf8>>)}.
-file("src/spruce/layout.gleam", 30).
?DOC(" Join blocks horizontally, aligning shorter blocks within the tallest block.\n").
-spec join_horizontal(pos(), list(binary())) -> binary().
join_horizontal(Pos, Blocks) ->
case Blocks of
[] ->
<<""/utf8>>;
_ ->
Block_infos = gleam@list:map(Blocks, fun block_info/1),
Target_height = max_block_height(Block_infos),
_pipe = Block_infos,
_pipe@1 = gleam@list:map(
_pipe,
fun(Block) -> normalize_block(Block, Target_height, Pos) end
),
join_normalized_blocks(_pipe@1, [])
end.
-file("src/spruce/layout.gleam", 45).
?DOC(" Place content in a region, preserving content larger than the requested size.\n").
-spec place(integer(), integer(), pos(), pos(), binary()) -> binary().
place(Width, Height, H, V, Content) ->
{Content_width, Content_height} = spruce@align:size(Content),
Region_width = gleam@int:max(Width, Content_width),
Region_height = gleam@int:max(Height, Content_height),
Lines = begin
_pipe = Content,
_pipe@1 = gleam@string:split(_pipe, <<"\n"/utf8>>),
gleam@list:map(_pipe@1, fun(Line) -> pad(Line, Region_width, H) end)
end,
_pipe@2 = position_lines(
Lines,
Content_height,
Region_width,
Region_height,
V
),
gleam@string:join(_pipe@2, <<"\n"/utf8>>).