use crate::{docs, Doc, DocAllocator, DocBuilder};
pub struct Affixes<'doc, D, A>
where
D: DocAllocator<'doc, A>,
{
prefix: DocBuilder<'doc, D, A>,
suffix: DocBuilder<'doc, D, A>,
nest: bool,
}
impl<'a, D, A> Clone for Affixes<'a, D, A>
where
A: Clone,
D: DocAllocator<'a, A> + 'a,
D::Doc: Clone,
{
fn clone(&self) -> Self {
Affixes {
prefix: self.prefix.clone(),
suffix: self.suffix.clone(),
nest: self.nest,
}
}
}
impl<'doc, D, A> Affixes<'doc, D, A>
where
D: DocAllocator<'doc, A>,
{
pub fn new(prefix: DocBuilder<'doc, D, A>, suffix: DocBuilder<'doc, D, A>) -> Self {
Affixes {
prefix,
suffix,
nest: false,
}
}
pub fn nest(mut self) -> Self {
self.nest = true;
self
}
}
pub struct BlockDoc<'doc, D, A>
where
D: DocAllocator<'doc, A>,
{
pub affixes: Vec<Affixes<'doc, D, A>>,
pub body: DocBuilder<'doc, D, A>,
}
impl<'doc, D, A> BlockDoc<'doc, D, A>
where
D: DocAllocator<'doc, A>,
D::Doc: Clone,
A: Clone,
{
pub fn format(self, nest: isize) -> DocBuilder<'doc, D, A> {
let arena = self.body.0;
let fail_on_multi_line = arena.fail().flat_alt(arena.nil());
(1..self.affixes.len() + 1)
.rev()
.map(|split| {
let (before, after) = self.affixes.split_at(split);
let last = before.len() == 1;
docs![
arena,
docs![
arena,
arena.concat(before.iter().map(|affixes| affixes.prefix.clone())),
if last {
arena.nil()
} else {
fail_on_multi_line.clone()
}
]
.group(),
docs![
arena,
after.iter().rev().cloned().fold(
docs![
arena,
self.body.clone(),
if !last
&& before
.iter()
.all(|affixes| matches!(&*affixes.prefix.1, Doc::Nil))
{
fail_on_multi_line.clone()
} else {
arena.nil()
},
]
.nest(nest)
.append(
arena.concat(after.iter().map(|affixes| affixes.suffix.clone()))
),
|acc, affixes| {
let mut doc = affixes.prefix.append(acc);
if affixes.nest {
doc = doc.nest(nest);
}
doc.group()
},
),
arena.concat(before.iter().map(|affixes| affixes.suffix.clone())),
]
.group(),
]
})
.fold(None::<DocBuilder<_, _>>, |acc, doc| {
Some(match acc {
None => doc,
Some(acc) => acc.union(doc),
})
})
.unwrap_or(self.body)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Arena;
#[test]
fn format_block() {
let arena = &Arena::<()>::new();
let mk_doc = || BlockDoc {
affixes: vec![
Affixes::new(docs![arena, "\\x y ->"], arena.nil()).nest(),
Affixes::new(docs![arena, arena.line(), "\\z ->"], arena.nil()).nest(),
Affixes::new(
docs![arena, arena.line(), "{"],
docs![arena, arena.line(), "}"],
)
.nest(),
],
body: docs![arena, arena.line(), "result"],
};
expect_test::expect![[r#"\x y -> \z -> { result }"#]]
.assert_eq(&mk_doc().format(4).1.pretty(40).to_string());
expect_test::expect![[r#"
\x y -> \z -> {
result
}"#]]
.assert_eq(&mk_doc().format(4).1.pretty(15).to_string());
expect_test::expect![[r#"
\x y -> \z ->
{
result
}"#]]
.assert_eq(&mk_doc().format(4).1.pretty(14).to_string());
expect_test::expect![[r#"
\x y ->
\z ->
{
result
}"#]]
.assert_eq(&mk_doc().format(4).1.pretty(12).to_string());
}
}