Current section

Files

Jump to
glam src glam@doc.erl
Raw

src/glam@doc.erl

-module(glam@doc).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([append/2, break/2, concat/1, append_docs/2, flex_break/2, force_break/1, from_string/1, group/1, join/2, concat_join/2, lines/1, nest/2, nest_docs/2, prepend/2, prepend_docs/2, to_string_builder/2, to_string/2]).
-export_type([document/0, mode/0]).
-opaque document() :: {line, integer()} |
{concat, list(document())} |
{text, binary(), integer()} |
{nest, document(), integer()} |
{force_break, document()} |
{break, binary(), binary()} |
{flex_break, binary(), binary()} |
{group, document()}.
-type mode() :: broken | force_broken | unbroken.
-spec append(document(), document()) -> document().
append(First, Second) ->
case First of
{concat, Docs} ->
{concat, gleam@list:append(Docs, [Second])};
_ ->
{concat, [First, Second]}
end.
-spec break(binary(), binary()) -> document().
break(Unbroken, Broken) ->
{break, Unbroken, Broken}.
-spec concat(list(document())) -> document().
concat(Docs) ->
{concat, Docs}.
-spec append_docs(document(), list(document())) -> document().
append_docs(First, Docs) ->
append(First, concat(Docs)).
-spec flex_break(binary(), binary()) -> document().
flex_break(Unbroken, Broken) ->
{flex_break, Unbroken, Broken}.
-spec force_break(document()) -> document().
force_break(Doc) ->
{force_break, Doc}.
-spec from_string(binary()) -> document().
from_string(String) ->
{text, String, gleam@string:length(String)}.
-spec group(document()) -> document().
group(Doc) ->
{group, Doc}.
-spec join(list(document()), document()) -> document().
join(Docs, Separator) ->
concat(gleam@list:intersperse(Docs, Separator)).
-spec concat_join(list(document()), list(document())) -> document().
concat_join(Docs, Separators) ->
join(Docs, concat(Separators)).
-spec lines(integer()) -> document().
lines(Size) ->
{line, Size}.
-spec nest(document(), integer()) -> document().
nest(Doc, Indentation) ->
{nest, Doc, Indentation}.
-spec nest_docs(list(document()), integer()) -> document().
nest_docs(Docs, Indentation) ->
{nest, concat(Docs), Indentation}.
-spec prepend(document(), document()) -> document().
prepend(First, Second) ->
case First of
{concat, Docs} ->
{concat, [Second | Docs]};
_ ->
{concat, [Second, First]}
end.
-spec prepend_docs(document(), list(document())) -> document().
prepend_docs(First, Docs) ->
prepend(First, concat(Docs)).
-spec fits(list({integer(), mode(), document()}), integer(), integer()) -> boolean().
fits(Docs, Max_width, Current_width) ->
case Docs of
_ when Current_width > Max_width ->
false;
[] ->
true;
[{Indent, Mode, Doc} | Rest] ->
case Doc of
{line, _} ->
true;
{force_break, _} ->
false;
{text, _, Length} ->
fits(Rest, Max_width, Current_width + Length);
{nest, Doc@1, I} ->
_pipe = [{Indent + I, Mode, Doc@1} | Rest],
fits(_pipe, Max_width, Current_width);
{break, Unbroken, _} ->
case Mode of
broken ->
true;
force_broken ->
true;
unbroken ->
fits(
Rest,
Max_width,
Current_width + gleam@string:length(Unbroken)
)
end;
{flex_break, Unbroken, _} ->
case Mode of
broken ->
true;
force_broken ->
true;
unbroken ->
fits(
Rest,
Max_width,
Current_width + gleam@string:length(Unbroken)
)
end;
{group, Doc@2} ->
fits(
[{Indent, Mode, Doc@2} | Rest],
Max_width,
Current_width
);
{concat, Docs@1} ->
_pipe@1 = gleam@list:map(
Docs@1,
fun(Doc@3) -> {Indent, Mode, Doc@3} end
),
_pipe@2 = gleam@list:append(_pipe@1, Rest),
fits(_pipe@2, Max_width, Current_width)
end
end.
-spec indentation(integer()) -> binary().
indentation(Size) ->
gleam@string:repeat(<<" "/utf8>>, Size).
-spec do_format(
gleam@string_builder:string_builder(),
integer(),
integer(),
list({integer(), mode(), document()})
) -> gleam@string_builder:string_builder().
do_format(Acc, Max_width, Current_width, Docs) ->
case Docs of
[] ->
Acc;
[{Indent, Mode, Doc} | Rest] ->
case Doc of
{line, Size} ->
_pipe = gleam@string_builder:append(
Acc,
gleam@string:repeat(<<"\n"/utf8>>, Size)
),
_pipe@1 = gleam@string_builder:append(
_pipe,
indentation(Indent)
),
do_format(_pipe@1, Max_width, Indent, Rest);
{flex_break, Unbroken, Broken} ->
New_unbroken_width = Current_width + gleam@string:length(
Unbroken
),
case fits(Rest, Max_width, New_unbroken_width) of
true ->
_pipe@2 = gleam@string_builder:append(Acc, Unbroken),
do_format(
_pipe@2,
Max_width,
New_unbroken_width,
Rest
);
false ->
_pipe@3 = gleam@string_builder:append(Acc, Broken),
_pipe@4 = gleam@string_builder:append(
_pipe@3,
<<"\n"/utf8>>
),
_pipe@5 = gleam@string_builder:append(
_pipe@4,
indentation(Indent)
),
do_format(_pipe@5, Max_width, Indent, Rest)
end;
{break, Unbroken@1, Broken@1} ->
case Mode of
unbroken ->
New_width = Current_width + gleam@string:length(
Unbroken@1
),
_pipe@6 = gleam@string_builder:append(
Acc,
Unbroken@1
),
do_format(_pipe@6, Max_width, New_width, Rest);
broken ->
_pipe@7 = gleam@string_builder:append(Acc, Broken@1),
_pipe@8 = gleam@string_builder:append(
_pipe@7,
<<"\n"/utf8>>
),
_pipe@9 = gleam@string_builder:append(
_pipe@8,
indentation(Indent)
),
do_format(_pipe@9, Max_width, Indent, Rest);
force_broken ->
_pipe@7 = gleam@string_builder:append(Acc, Broken@1),
_pipe@8 = gleam@string_builder:append(
_pipe@7,
<<"\n"/utf8>>
),
_pipe@9 = gleam@string_builder:append(
_pipe@8,
indentation(Indent)
),
do_format(_pipe@9, Max_width, Indent, Rest)
end;
{force_break, Doc@1} ->
_pipe@10 = [{Indent, force_broken, Doc@1} | Rest],
do_format(Acc, Max_width, Current_width, _pipe@10);
{concat, Docs@1} ->
_pipe@11 = gleam@list:map(
Docs@1,
fun(Doc@2) -> {Indent, Mode, Doc@2} end
),
_pipe@12 = gleam@list:append(_pipe@11, Rest),
do_format(Acc, Max_width, Current_width, _pipe@12);
{group, Doc@3} ->
_pipe@13 = case fits(
[{Indent, unbroken, Doc@3}],
Max_width,
Current_width
) of
true ->
{Indent, unbroken, Doc@3};
false ->
{Indent, broken, Doc@3}
end,
_pipe@14 = gleam@list:prepend(Rest, _pipe@13),
do_format(Acc, Max_width, Current_width, _pipe@14);
{nest, Doc@4, I} ->
_pipe@15 = [{Indent + I, Mode, Doc@4} | Rest],
do_format(Acc, Max_width, Current_width, _pipe@15);
{text, Text, Length} ->
_pipe@16 = gleam@string_builder:append(Acc, Text),
do_format(_pipe@16, Max_width, Current_width + Length, Rest)
end
end.
-spec to_string_builder(document(), integer()) -> gleam@string_builder:string_builder().
to_string_builder(Doc, Width) ->
do_format(gleam@string_builder:new(), Width, 0, [{0, unbroken, Doc}]).
-spec to_string(document(), integer()) -> binary().
to_string(Doc, Width) ->
_pipe = to_string_builder(Doc, Width),
gleam@string_builder:to_string(_pipe).