1use crate::{docs, Doc, DocAllocator, DocBuilder};
6
7pub struct Affixes<'doc, D, A>
8where
9 D: DocAllocator<'doc, A>,
10{
11 prefix: DocBuilder<'doc, D, A>,
12 suffix: DocBuilder<'doc, D, A>,
13 nest: bool,
14}
15
16impl<'a, D, A> Clone for Affixes<'a, D, A>
17where
18 A: Clone,
19 D: DocAllocator<'a, A> + 'a,
20 D::Doc: Clone,
21{
22 fn clone(&self) -> Self {
23 Affixes {
24 prefix: self.prefix.clone(),
25 suffix: self.suffix.clone(),
26 nest: self.nest,
27 }
28 }
29}
30
31impl<'doc, D, A> Affixes<'doc, D, A>
32where
33 D: DocAllocator<'doc, A>,
34{
35 pub fn new(prefix: DocBuilder<'doc, D, A>, suffix: DocBuilder<'doc, D, A>) -> Self {
36 Affixes {
37 prefix,
38 suffix,
39 nest: false,
40 }
41 }
42
43 pub fn nest(mut self) -> Self {
44 self.nest = true;
45 self
46 }
47}
48
49pub struct BlockDoc<'doc, D, A>
79where
80 D: DocAllocator<'doc, A>,
81{
82 pub affixes: Vec<Affixes<'doc, D, A>>,
83 pub body: DocBuilder<'doc, D, A>,
84}
85
86impl<'doc, D, A> BlockDoc<'doc, D, A>
87where
88 D: DocAllocator<'doc, A>,
89 D::Doc: Clone,
90 A: Clone,
91{
92 pub fn format(self, nest: isize) -> DocBuilder<'doc, D, A> {
93 let arena = self.body.0;
94
95 let fail_on_multi_line = arena.fail().flat_alt(arena.nil());
96
97 (1..self.affixes.len() + 1)
98 .rev()
99 .map(|split| {
100 let (before, after) = self.affixes.split_at(split);
101 let last = before.len() == 1;
102 docs![
103 arena,
104 docs![
105 arena,
106 arena.concat(before.iter().map(|affixes| affixes.prefix.clone())),
107 if last {
108 arena.nil()
109 } else {
110 fail_on_multi_line.clone()
111 }
112 ]
113 .group(),
114 docs![
115 arena,
116 after.iter().rev().cloned().fold(
117 docs![
118 arena,
119 self.body.clone(),
120 if !last
123 && before
124 .iter()
125 .all(|affixes| matches!(&*affixes.prefix.1, Doc::Nil))
126 {
127 fail_on_multi_line.clone()
128 } else {
129 arena.nil()
130 },
131 ]
132 .nest(nest)
133 .append(
134 arena.concat(after.iter().map(|affixes| affixes.suffix.clone()))
135 ),
136 |acc, affixes| {
137 let mut doc = affixes.prefix.append(acc);
138 if affixes.nest {
139 doc = doc.nest(nest);
140 }
141 doc.group()
142 },
143 ),
144 arena.concat(before.iter().map(|affixes| affixes.suffix.clone())),
145 ]
146 .group(),
147 ]
148 })
149 .fold(None::<DocBuilder<_, _>>, |acc, doc| {
150 Some(match acc {
151 None => doc,
152 Some(acc) => acc.union(doc),
153 })
154 })
155 .unwrap_or(self.body)
156 }
157}
158
159#[cfg(test)]
160mod tests {
161 use super::*;
162
163 use crate::Arena;
164
165 #[test]
166 fn format_block() {
167 let arena = &Arena::<()>::new();
168 let mk_doc = || BlockDoc {
169 affixes: vec![
170 Affixes::new(docs![arena, "\\x y ->"], arena.nil()).nest(),
171 Affixes::new(docs![arena, arena.line(), "\\z ->"], arena.nil()).nest(),
172 Affixes::new(
173 docs![arena, arena.line(), "{"],
174 docs![arena, arena.line(), "}"],
175 )
176 .nest(),
177 ],
178 body: docs![arena, arena.line(), "result"],
179 };
180 expect_test::expect![[r#"\x y -> \z -> { result }"#]]
181 .assert_eq(&mk_doc().format(4).1.pretty(40).to_string());
182 expect_test::expect![[r#"
183\x y -> \z -> {
184 result
185}"#]]
186 .assert_eq(&mk_doc().format(4).1.pretty(15).to_string());
187 expect_test::expect![[r#"
188\x y -> \z ->
189 {
190 result
191 }"#]]
192 .assert_eq(&mk_doc().format(4).1.pretty(14).to_string());
193 expect_test::expect![[r#"
194\x y ->
195 \z ->
196 {
197 result
198 }"#]]
199 .assert_eq(&mk_doc().format(4).1.pretty(12).to_string());
200 }
201}