///|
/// How the output should be laid out.
pub(all) enum Style {
/// One line, with `«»`, `;` and `,` doing the grouping. Reads back the same
/// however it is later re-indented, which is what makes it the safe default
/// for writing a shrubbery down to be read later.
Flat
/// Line breaks and indentation, falling back to one line where it fits.
Pretty
/// Pretty, but still line- and column-insensitive: `«»` rather than
/// indentation.
Armoured
/// Pretty, and preferring line breaks to `«»` where either would do.
PreferMultiline
} derive(Eq)
///|
/// Write a shrubbery.
///
/// The contract is round-tripping: reading the output back with `parse`
/// produces the same tree. `width` applies to the pretty styles; `None` there
/// means "never break a line that could be one", and `Some(0)` means "break
/// every line that could be broken".
pub fn write(
node : @ast.Node,
style? : Style = Flat,
width? : Int? = None,
) -> String {
match style {
Flat => {
let buf = StringBuilder()
write_flat(node, buf, before_block=false)
buf.to_string()
}
_ => @doc.render_string(to_doc(node, style~), width~)
}
}
///|
/// The layout document for a shrubbery, before a width is chosen.
///
/// Exposed because the document is the interesting object: it describes every
/// legal layout at once, and a consumer with its own renderer -- or its own
/// idea of what fits -- can take it from here.
pub fn to_doc(node : @ast.Node, style? : Style = Pretty) -> @doc.Doc {
let target = match style {
Armoured => ArmorDoc
PreferMultiline => MultiDoc
_ => PlainDoc
}
let r = build(node, target, non_tail=false, head=true, before_block=false)
match r.single {
Some(single) => Or(single, r.multi)
None => r.multi
}
}
///|
/// Which of the three document readings is being built.
priv enum Target {
/// Line breaks where they help, `«»` where they are needed.
PlainDoc
/// `«»` everywhere, so the output survives being re-indented.
ArmorDoc
/// Line breaks in preference to `«»`.
MultiDoc
}
///|
/// What one term contributes.
priv struct Built {
/// The single-line reading, when there is one. `None` in `MultiDoc` for a
/// term that must break.
single : @doc.Doc?
/// The multi-line reading.
multi : @doc.Doc
/// Whether an unquoted `'` appears in either, which decides whether a
/// surrounding `'…'` needs its guillemets.
quotes : Bool
/// Whether an enclosing `|` can do without `«»` around the single-line form.
is_atom : Bool
}
///|
/// Both readings the same, and is_atom: an atom.
fn twice(text : String) -> Built {
{ single: Some(Str(text)), multi: Str(text), quotes: false, is_atom: true, }
}