Skip to main content

src/spruce/group.gleam

//// Depth-in-context grouped output helpers.

import gleam/io
import gleam/list
import gleam/string
import spruce.{type Spruce}
import spruce/internal/layout
import spruce/palette
import spruce/style
import spruce/symbol

/// Print a group title, then run `body` with a context indented one level deeper.
///
/// This is the eager, streaming form of grouping: the title prints immediately
/// and `body` runs right away, so its output appears as work happens and it may
/// perform IO and return any value. For deferred, pipe-composable grouping that
/// buffers output instead, see `spruce/output.group`.
pub fn group(sp: Spruce, title: String, body: fn(Spruce) -> result) -> result {
  io.println(render_title(sp, title))
  body(spruce.indented(sp))
}

/// Render a group title line (indent prefix + styled marker + title), the same
/// line that `group` prints. Exposed so buffered output (`spruce/output`) can
/// compose group titles without duplicating the styling.
pub fn render_title(sp: Spruce, title: String) -> String {
  layout.indent_prefix(sp) <> title_line(sp, title)
}

fn title_line(sp: Spruce, title: String) -> String {
  case spruce.supports_color(sp) {
    False -> symbol.arrow <> " " <> title
    True ->
      style.render(sp, palette.hash(sp, title), symbol.arrow)
      <> " "
      <> style.render(sp, style.new() |> style.bold, title)
  }
}

/// Prefix every line in `text` with two spaces for each indent level.
pub fn indent(text: String, level: Int) -> String {
  let prefix = string.repeat("  ", level)

  text
  |> string.split("\n")
  |> list.map(fn(line) { prefix <> line })
  |> string.join("\n")
}