// A structured diagnostic message.
//
// The tree carries the message's STRUCTURE; the theme and the width arrive only
// at render time. One value therefore renders three ways -- to a themed
// terminal, and flattened for the JSON and short formats -- with no branching
// at construction.
///|
/// A diagnostic message.
pub(all) enum Message {
Empty
/// Prose, reflowed on spaces and hard-broken on newlines.
Words(String)
/// One atom: its style, whether to quote it when the theme is uncoloured,
/// and its text.
///
/// The quoting flag is the whole emphasis mechanism. An emphasized atom is
/// shown in its colour when there is colour and wrapped in `'...'` when there
/// is not -- so JSON and short output, which are never coloured, are always
/// quoted, and an interactive terminal is coloured and unquoted.
Atom(@colors.Style, Bool, String)
/// The escape hatch: run a callback against the render-time printer.
///
/// This is how a type or an instruction gets rendered through the shared
/// pretty-printer without this package having to know about the AST.
Raw((@printer.Printer, @colors.Theme) -> Unit)
/// Juxtapose with no space.
Seq(Message, Message)
/// Juxtapose with a soft space, which is a wrap point.
Sep(Message, Message)
/// Lay out as a keep-together box, indented when broken.
Group(Message)
}
///|
/// Prose.
///
/// Splits on spaces into words joined by soft breaks, so it reflows at the
/// render width; an embedded newline is a hard break.
pub fn text(s : String) -> Message {
Words(s)
}
///|
/// An emphasized source identifier.
pub fn ident(s : String) -> Message {
Atom(Identifier, true, s)
}
///|
/// An emphasized inline code token -- an instruction, an operator, a literal.
pub fn code(s : String) -> Message {
Atom(Keyword, true, s)
}
///|
/// An emphasized type name given as a string.
pub fn type_(s : String) -> Message {
Atom(Type, true, s)
}
///|
/// One unbreakable styled atom, never quoted.
pub fn styled(style : @colors.Style, s : String) -> Message {
Atom(style, false, s)
}
///|
pub fn int(n : Int) -> Message {
Atom(Constant, false, n.to_string())
}
///|
pub fn int64(n : Int64) -> Message {
Atom(Constant, false, n.to_string())
}
///|
/// `n` as an UNSIGNED 64-bit integer, so a value with the high bit set prints
/// as its magnitude rather than as a negative number.
pub fn uint64(n : Int64) -> Message {
Atom(Constant, false, n.reinterpret_as_uint64().to_string())
}
///|
pub fn bool_(b : Bool) -> Message {
Atom(Constant, false, b.to_string())
}
///|
/// Run an imperative callback against the render-time printer and theme.
pub fn raw(f : (@printer.Printer, @colors.Theme) -> Unit) -> Message {
Raw(f)
}
///|
/// Juxtapose with no space between.
pub fn Message::seq(self : Message, b : Message) -> Message {
Seq(self, b)
}
///|
/// Juxtapose with a soft (wrap-point) space between.
pub fn Message::sep(self : Message, b : Message) -> Message {
Sep(self, b)
}
///|
pub fn concat(l : Array[Message]) -> Message {
let mut acc : Message = Empty
for m in l {
acc = Seq(acc, m)
}
acc
}
///|
/// Lay out as a keep-together box.
///
/// Used to wrap a rendered type so it does not reflow word by word.
pub fn group(m : Message) -> Message {
Group(m)
}
///|
/// `"a, b or c"` -- commas, with `conj` before the last item.
pub fn enumerate(items : Array[Message], conj? : String = "or") -> Message {
match items.length() {
0 => Empty
1 => items[0]
n => {
let mut commad = items[0]
for i in 1..<(n - 1) {
commad = Seq(commad, Sep(text(","), items[i]))
}
Sep(Sep(commad, text(conj)), items[n - 1])
}
}
}