///|
/// One piece of a [`pretty`] template: either literal text (which may contain
/// newlines) or an embedded document.
pub(all) enum Piece {
/// Literal text. Every `\n` in it starts a new line of the template.
Str(String)
/// An already-built document, spliced in at this position.
Val(Doc)
} derive(Debug)
///|
/// Builds a document from a sequence of literal-text and document pieces, the
/// way a string template would: the pieces are cut into lines at every newline
/// in the literal text, the lines are joined with [`vert`], and the pieces of
/// each line are joined with [`horz`].
///
/// This is the MoonBit stand-in for the JavaScript library's tagged
/// `` pretty`...` `` template.
///
/// ```mbt check
/// test {
/// let c = txt("a == b")
/// let t = txt("a << 2")
/// let e = txt("a + b")
/// let doc = pretty([
/// Str("if ("),
/// Val(c),
/// Str(") {\n "),
/// Val(t),
/// Str("\n} else {\n "),
/// Val(e),
/// Str("\n}"),
/// ])
/// inspect(
/// doc.display_string(),
/// content=(
/// #|if (a == b) {
/// #| a << 2
/// #|} else {
/// #| a + b
/// #|}
/// ),
/// )
/// }
/// ```
///
/// Because every line is a [`horz`], an embedded document that spans several
/// lines is indented to line up with where it started.
pub fn pretty(pieces : Array[Piece]) -> Doc {
let lines = []
let line_parts = []
for piece in pieces {
match piece {
Val(doc) => line_parts.push(doc)
Str(text) =>
for i, part in text.split("\n").collect() {
// Every newline in the literal text closes off the current line.
if i != 0 {
lines.push(horz(line_parts))
line_parts.clear()
}
line_parts.push(txt(part.to_owned()))
}
}
}
lines.push(horz(line_parts))
vert(lines)
}
///|
/// Like [`txt`], but accepts newlines: the text is split into lines which are
/// joined with [`vert`].
///
/// ```mbt check
/// test {
/// inspect(
/// horz([txt("> "), txt_lines("one\ntwo")]).display_string(),
/// content=(
/// #|> one
/// #| two
/// ),
/// )
/// }
/// ```
pub fn txt_lines(text : String) -> Doc {
pretty([Str(text)])
}