catlog/zero/
pretty.rs

1//! Helper functions for writing pretty-printers.
2
3use pretty::RcDoc;
4use std::{borrow::Cow, fmt, ops};
5
6/// A type that can be pretty-printed.
7pub trait ToDoc {
8    /// Pretty prints the object, returning a doc.
9    fn to_doc<'a>(&self) -> D<'a>;
10}
11
12/// A wrapper around RcDoc with new methods and a shorter name.
13///
14/// In particular, we implement [ops::Add], which enables docs to be
15/// concatenated with `+`.
16#[derive(Clone)]
17pub struct D<'a>(pub RcDoc<'a, ()>);
18
19impl<'a> ops::Add for D<'a> {
20    type Output = D<'a>;
21
22    fn add(self, rhs: Self) -> Self::Output {
23        D(self.0.append(rhs.0))
24    }
25}
26
27/// Creates a text doc.
28pub fn t<'a, U: Into<Cow<'a, str>>>(data: U) -> D<'a> {
29    D(RcDoc::text(data))
30}
31
32/// Creates a soft line break (becomes a space when grouped).
33pub fn s<'a>() -> D<'a> {
34    D(RcDoc::line())
35}
36
37/// Creates a hard line break (always a newline).
38pub fn hardline<'a>() -> D<'a> {
39    D(RcDoc::hardline())
40}
41
42/// Creates a unary operator applied to one argument in [fnotation].
43pub fn unop<'a>(op: D<'a>, arg: D<'a>) -> D<'a> {
44    (op + s() + arg).group()
45}
46
47/// Creates a binary operator applied to two arguments.
48pub fn binop<'a>(op: D<'a>, l: D<'a>, r: D<'a>) -> D<'a> {
49    ((l + s() + op).group() + (s() + r).indented()).group()
50}
51
52/// Creates a tuple in [fnotation]: (`[x, y, z, ...]`).
53pub fn tuple<'a, I: IntoIterator<Item = D<'a>>>(i: I) -> D<'a> {
54    D(RcDoc::intersperse(i.into_iter().map(|d| d.0.group()), (t(",") + s()).0))
55        .brackets()
56        .group()
57}
58
59/// Intersperses documents with the given separator.
60pub fn intersperse<'a, I: IntoIterator<Item = D<'a>>>(i: I, sep: D<'a>) -> D<'a> {
61    D(RcDoc::intersperse(i.into_iter().map(|d| d.0), sep.0))
62}
63
64impl<'a> D<'a> {
65    /// Try to lay out this document as a group; either on one line or uniformly split.
66    pub fn group(self) -> D<'a> {
67        D(self.0.group())
68    }
69
70    /// Surround this document with parentheses.
71    pub fn parens(self) -> D<'a> {
72        t("(") + self.group() + t(")")
73    }
74
75    /// Increase the indentation level.
76    pub fn indented(self) -> Self {
77        D(self.0.nest(2))
78    }
79
80    /// Surround this document with brackets.
81    pub fn brackets(self) -> D<'a> {
82        t("[") + self.indented() + t("]")
83    }
84
85    /// Use this to print a document.
86    pub fn pretty(&self) -> impl fmt::Display {
87        self.0.pretty(80)
88    }
89}