-module(spruce@output). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/spruce/output.gleam"). -export([new/1, context/1, append/2, text/2, blank/1, group/3, to_string/1, print/1]). -export_type([output/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( " Pipeable output accumulation.\n" "\n" " An `Output` threads a `Spruce` context and a buffer of rendered blocks\n" " through a pipeline, so several renderers compose with `|>` and emit\n" " together. It stays pure: nothing is printed until `print`, and the context\n" " is threaded for you so each renderer sees the right color level and indent\n" " depth.\n" "\n" " ```gleam\n" " import spruce\n" " import spruce/message\n" " import spruce/output\n" "\n" " pub fn main() {\n" " let sp = spruce.detect()\n" "\n" " output.new(sp)\n" " |> output.append(message.start(_, \"compiling\"))\n" " |> output.group(\"Tests\", fn(o) {\n" " o |> output.append(message.info(_, \"running\"))\n" " })\n" " |> output.print\n" " }\n" " ```\n" "\n" " `append` accepts any `Spruce -> String` renderer via a `_` capture, so it\n" " works with every spruce module without per-type variants. For eager,\n" " streaming grouping that prints as work happens and can return a value, use\n" " `spruce/group.group` instead.\n" ). -opaque output() :: {output, spruce:spruce(), list(binary())}. -file("src/spruce/output.gleam", 45). ?DOC(" Start an empty output that renders with `sp`.\n"). -spec new(spruce:spruce()) -> output(). new(Sp) -> {output, Sp, []}. -file("src/spruce/output.gleam", 51). ?DOC( " The context the output renders with. Reflects the current group depth inside\n" " a `group` body.\n" ). -spec context(output()) -> spruce:spruce(). context(Output) -> erlang:element(2, Output). -file("src/spruce/output.gleam", 58). ?DOC( " Append a rendered block produced by `render`, which receives the output's\n" " context. Works with any `Spruce -> String` renderer via a `_` capture, e.g.\n" " `output.append(message.success(_, \"done\"))`.\n" ). -spec append(output(), fun((spruce:spruce()) -> binary())) -> output(). append(Output, Render) -> {output, erlang:element(2, Output), [Render(erlang:element(2, Output)) | erlang:element(3, Output)]}. -file("src/spruce/output.gleam", 63). ?DOC(" Append a raw string as-is, without rendering.\n"). -spec text(output(), binary()) -> output(). text(Output, Text) -> {output, erlang:element(2, Output), [Text | erlang:element(3, Output)]}. -file("src/spruce/output.gleam", 68). ?DOC(" Append a blank line.\n"). -spec blank(output()) -> output(). blank(Output) -> {output, erlang:element(2, Output), [<<""/utf8>> | erlang:element(3, Output)]}. -file("src/spruce/output.gleam", 75). ?DOC( " Append a styled group title, then run `body` with the output's context\n" " indented one level deeper. Blocks appended inside `body` nest under the\n" " title. Unlike `spruce/group.group`, this buffers output rather than printing.\n" ). -spec group(output(), binary(), fun((output()) -> output())) -> output(). group(Output, Title, Body) -> Titled = text( Output, spruce@group:render_title(erlang:element(2, Output), Title) ), Body_output = Body( {output, spruce:indented(erlang:element(2, Output)), erlang:element(3, Titled)} ), {output, erlang:element(2, Output), erlang:element(3, Body_output)}. -file("src/spruce/output.gleam", 87). ?DOC(" Render the accumulated output to a single string, blocks joined by newlines.\n"). -spec to_string(output()) -> binary(). to_string(Output) -> _pipe = erlang:element(3, Output), _pipe@1 = lists:reverse(_pipe), gleam@string:join(_pipe@1, <<"\n"/utf8>>). -file("src/spruce/output.gleam", 94). ?DOC(" Print the accumulated output to stdout.\n"). -spec print(output()) -> nil. print(Output) -> gleam_stdlib:println(to_string(Output)).