Skip to main content

candid/pretty/
utils.rs

1use pretty::RcDoc;
2
3pub const INDENT_SPACE: isize = 2;
4pub const LINE_WIDTH: usize = 80;
5
6fn is_empty(doc: &RcDoc) -> bool {
7    use pretty::Doc::*;
8    match &**doc {
9        Nil => true,
10        FlatAlt(t1, t2) => is_empty(t2) || is_empty(t1),
11        Union(t1, t2) => is_empty(t1) && is_empty(t2),
12        Group(t) | Nest(_, t) | Annotated((), t) => is_empty(t),
13        _ => false,
14    }
15}
16
17pub fn enclose<'a>(left: &'a str, doc: RcDoc<'a>, right: &'a str) -> RcDoc<'a> {
18    if is_empty(&doc) {
19        RcDoc::text(left).append(right)
20    } else {
21        RcDoc::text(left)
22            .append(RcDoc::line_())
23            .append(doc)
24            .nest(INDENT_SPACE)
25            .append(RcDoc::line_())
26            .append(right)
27            .group()
28    }
29}
30
31pub fn enclose_space<'a>(left: &'a str, doc: RcDoc<'a>, right: &'a str) -> RcDoc<'a> {
32    if is_empty(&doc) {
33        RcDoc::text(left).append(right)
34    } else {
35        RcDoc::text(left)
36            .append(RcDoc::line())
37            .append(doc)
38            .nest(INDENT_SPACE)
39            .append(RcDoc::line())
40            .append(right)
41            .group()
42    }
43}
44
45/// Intersperse the separator between each item in `docs`.
46pub fn strict_concat<'a, D>(docs: D, sep: &'a str) -> RcDoc<'a>
47where
48    D: Iterator<Item = RcDoc<'a>>,
49{
50    RcDoc::intersperse(docs, RcDoc::text(sep).append(RcDoc::line()))
51}
52
53/// Append the separator to each item in `docs`. If it is displayed in a single line, omit the last separator.
54pub fn concat<'a, D>(docs: D, sep: &'a str) -> RcDoc<'a>
55where
56    D: Iterator<Item = RcDoc<'a>>,
57{
58    let mut docs = docs.peekable();
59    if docs.peek().is_none() {
60        return RcDoc::nil();
61    }
62    let singleline = RcDoc::intersperse(docs, RcDoc::text(sep).append(RcDoc::line()));
63    let multiline = singleline.clone().append(sep);
64    multiline.flat_alt(singleline)
65}
66
67pub fn lines<'a, D>(docs: D) -> RcDoc<'a>
68where
69    D: Iterator<Item = RcDoc<'a>>,
70{
71    RcDoc::concat(docs.map(|doc| doc.append(RcDoc::hardline())))
72}
73
74pub fn kwd<U: std::fmt::Display + ?Sized>(str: &U) -> RcDoc<'_> {
75    RcDoc::as_string(str).append(RcDoc::space())
76}
77
78pub fn str(str: &str) -> RcDoc<'_> {
79    RcDoc::text(str)
80}
81
82pub fn ident(id: &str) -> RcDoc<'_> {
83    kwd(id)
84}
85
86pub fn quote_ident(id: &str) -> RcDoc<'_> {
87    str("'")
88        .append(format!("{}", id.escape_debug()))
89        .append("'")
90        .append(RcDoc::space())
91}
92
93#[cfg(test)]
94mod test {
95    use super::*;
96
97    #[test]
98    fn concat_empty() {
99        let t = concat(vec![].into_iter(), ",")
100            .pretty(LINE_WIDTH)
101            .to_string();
102        assert_eq!(t, "");
103    }
104}