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
45pub 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
53pub 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}