pretty/
lib.rs

1//! This crate defines a
2//! [Wadler-style](http://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf)
3//! pretty-printing API.
4//!
5//! Start with the static functions of [Doc](enum.Doc.html).
6//!
7//! ## Quick start
8//!
9//! Let's pretty-print simple sexps!  We want to pretty print sexps like
10//!
11//! ```lisp
12//! (1 2 3)
13//! ```
14//! or, if the line would be too long, like
15//!
16//! ```lisp
17//! ((1)
18//!  (2 3)
19//!  (4 5 6))
20//! ```
21//!
22//! A _simple symbolic expression_ consists of a numeric _atom_ or a nested ordered _list_ of
23//! symbolic expression children.
24//!
25//! ```rust
26//! # use pretty::*;
27//! enum SExp {
28//!     Atom(u32),
29//!     List(Vec<SExp>),
30//! }
31//! use SExp::*;
32//! # fn main() { }
33//! ```
34//!
35//! We define a simple conversion to a [Doc](enum.Doc.html).  Atoms are rendered as strings; lists
36//! are recursively rendered, with spaces between children where appropriate.  Children are
37//! [nested]() and [grouped](), allowing them to be laid out in a single line as appropriate.
38//!
39//! ```rust
40//! # use pretty::*;
41//! # enum SExp {
42//! #     Atom(u32),
43//! #     List(Vec<SExp>),
44//! # }
45//! # use SExp::*;
46//! impl SExp {
47//!     /// Return a pretty printed format of self.
48//!     pub fn to_doc(&self) -> RcDoc<()> {
49//!         match *self {
50//!             Atom(ref x) => RcDoc::as_string(x),
51//!             List(ref xs) =>
52//!                 RcDoc::text("(")
53//!                     .append(RcDoc::intersperse(xs.into_iter().map(|x| x.to_doc()), Doc::line()).nest(1).group())
54//!                     .append(RcDoc::text(")"))
55//!         }
56//!     }
57//! }
58//! # fn main() { }
59//! ```
60//!
61//! Next, we convert the [Doc](enum.Doc.html) to a plain old string.
62//!
63//! ```rust
64//! # use pretty::*;
65//! # enum SExp {
66//! #     Atom(u32),
67//! #     List(Vec<SExp>),
68//! # }
69//! # use SExp::*;
70//! # impl SExp {
71//! #     /// Return a pretty printed format of self.
72//! #     pub fn to_doc(&self) -> BoxDoc<()> {
73//! #         match *self {
74//! #             Atom(ref x) => BoxDoc::as_string(x),
75//! #             List(ref xs) =>
76//! #                 BoxDoc::text("(")
77//! #                     .append(BoxDoc::intersperse(xs.into_iter().map(|x| x.to_doc()), Doc::line()).nest(1).group())
78//! #                     .append(BoxDoc::text(")"))
79//! #         }
80//! #     }
81//! # }
82//! impl SExp {
83//!     pub fn to_pretty(&self, width: usize) -> String {
84//!         let mut w = Vec::new();
85//!         self.to_doc().render(width, &mut w).unwrap();
86//!         String::from_utf8(w).unwrap()
87//!     }
88//! }
89//! # fn main() { }
90//! ```
91//!
92//! And finally we can test that the nesting and grouping behaves as we expected.
93//!
94//! ```rust
95//! # use pretty::*;
96//! # enum SExp {
97//! #     Atom(u32),
98//! #     List(Vec<SExp>),
99//! # }
100//! # use SExp::*;
101//! # impl SExp {
102//! #     /// Return a pretty printed format of self.
103//! #     pub fn to_doc(&self) -> BoxDoc<()> {
104//! #         match *self {
105//! #             Atom(ref x) => BoxDoc::as_string(x),
106//! #             List(ref xs) =>
107//! #                 BoxDoc::text("(")
108//! #                     .append(BoxDoc::intersperse(xs.into_iter().map(|x| x.to_doc()), Doc::line()).nest(1).group())
109//! #                     .append(BoxDoc::text(")"))
110//! #         }
111//! #     }
112//! # }
113//! # impl SExp {
114//! #     pub fn to_pretty(&self, width: usize) -> String {
115//! #         let mut w = Vec::new();
116//! #         self.to_doc().render(width, &mut w).unwrap();
117//! #         String::from_utf8(w).unwrap()
118//! #     }
119//! # }
120//! # fn main() {
121//! let atom = SExp::Atom(5);
122//! assert_eq!("5", atom.to_pretty(10));
123//! let list = SExp::List(vec![SExp::Atom(1), SExp::Atom(2), SExp::Atom(3)]);
124//! assert_eq!("(1 2 3)", list.to_pretty(10));
125//! assert_eq!("\
126//! (1
127//!  2
128//!  3)", list.to_pretty(5));
129//! # }
130//! ```
131//!
132//! ## Advanced usage
133//!
134//! There's a more efficient pattern that uses the [DocAllocator](trait.DocAllocator.html) trait, as
135//! implemented by [BoxAllocator](struct.BoxAllocator.html), to allocate
136//! [DocBuilder](struct.DocBuilder.html) instances.  See
137//! [examples/trees.rs](https://github.com/freebroccolo/pretty.rs/blob/master/examples/trees.rs#L39)
138//! for this approach.
139
140#[cfg(feature = "termcolor")]
141pub extern crate termcolor;
142
143use std::{
144    borrow::Cow,
145    convert::TryInto,
146    fmt, io,
147    ops::{Add, AddAssign, Deref},
148    rc::Rc,
149};
150
151#[cfg(feature = "termcolor")]
152use termcolor::{ColorSpec, WriteColor};
153
154pub mod block;
155mod render;
156
157pub use self::block::{Affixes, BlockDoc};
158#[cfg(feature = "termcolor")]
159pub use self::render::TermColored;
160pub use self::render::{FmtWrite, IoWrite, Render, RenderAnnotated};
161
162/// The concrete document type. This type is not meant to be used directly. Instead use the static
163/// functions on `Doc` or the methods on an `DocAllocator`.
164///
165/// The `T` parameter is used to abstract over pointers to `Doc`. See `RefDoc` and `BoxDoc` for how
166/// it is used
167#[derive(Clone)]
168pub enum Doc<'a, T, A = ()>
169where
170    T: DocPtr<'a, A>,
171{
172    Nil,
173    Append(T, T),
174    Group(T),
175    FlatAlt(T, T),
176    Nest(isize, T),
177    Hardline,
178    // Stores the length of a string document that is not just ascii
179    RenderLen(usize, T),
180    OwnedText(Box<str>),
181    BorrowedText(&'a str),
182    SmallText(SmallText),
183    Annotated(A, T),
184    Union(T, T),
185    Column(T::ColumnFn),
186    Nesting(T::ColumnFn),
187    Fail,
188}
189
190impl<'a, T, A> Default for Doc<'a, T, A>
191where
192    T: DocPtr<'a, A>,
193{
194    fn default() -> Self {
195        Self::Nil
196    }
197}
198
199pub type SmallText = arrayvec::ArrayString<[u8; 22]>;
200
201fn append_docs<'a, 'd, T, A>(
202    mut doc: &'d Doc<'a, T, A>,
203    consumer: &mut impl FnMut(&'d Doc<'a, T, A>),
204) where
205    T: DocPtr<'a, A>,
206{
207    loop {
208        match doc {
209            Doc::Append(l, r) => {
210                append_docs(l, consumer);
211                doc = r;
212            }
213            _ => break consumer(doc),
214        }
215    }
216}
217
218impl<'a, T, A> fmt::Debug for Doc<'a, T, A>
219where
220    T: DocPtr<'a, A> + fmt::Debug,
221    A: fmt::Debug,
222{
223    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
224        let is_line = |doc: &Doc<'a, T, A>| match doc {
225            Doc::FlatAlt(x, y) => {
226                matches!((&**x, &**y), (Doc::Hardline, Doc::BorrowedText(" ")))
227            }
228            _ => false,
229        };
230        let is_line_ = |doc: &Doc<'a, T, A>| match doc {
231            Doc::FlatAlt(x, y) => {
232                matches!((&**x, &**y), (Doc::Hardline, Doc::Nil))
233            }
234            _ => false,
235        };
236        match self {
237            Doc::Nil => f.debug_tuple("Nil").finish(),
238            Doc::Append(..) => {
239                let mut f = f.debug_list();
240                append_docs(self, &mut |doc| {
241                    f.entry(doc);
242                });
243                f.finish()
244            }
245            _ if is_line(self) => f.debug_tuple("Line").finish(),
246            _ if is_line_(self) => f.debug_tuple("Line_").finish(),
247            Doc::FlatAlt(ref x, ref y) => f.debug_tuple("FlatAlt").field(x).field(y).finish(),
248            Doc::Group(ref doc) => {
249                if is_line(self) {
250                    return f.debug_tuple("SoftLine").finish();
251                }
252                if is_line_(self) {
253                    return f.debug_tuple("SoftLine_").finish();
254                }
255                f.debug_tuple("Group").field(doc).finish()
256            }
257            Doc::Nest(off, ref doc) => f.debug_tuple("Nest").field(&off).field(doc).finish(),
258            Doc::Hardline => f.debug_tuple("Hardline").finish(),
259            Doc::RenderLen(_, d) => d.fmt(f),
260            Doc::OwnedText(ref s) => s.fmt(f),
261            Doc::BorrowedText(ref s) => s.fmt(f),
262            Doc::SmallText(ref s) => s.fmt(f),
263            Doc::Annotated(ref ann, ref doc) => {
264                f.debug_tuple("Annotated").field(ann).field(doc).finish()
265            }
266            Doc::Union(ref l, ref r) => f.debug_tuple("Union").field(l).field(r).finish(),
267            Doc::Column(_) => f.debug_tuple("Column(..)").finish(),
268            Doc::Nesting(_) => f.debug_tuple("Nesting(..)").finish(),
269            Doc::Fail => f.debug_tuple("Fail").finish(),
270        }
271    }
272}
273
274macro_rules! impl_doc {
275    ($name: ident, $ptr: ident, $allocator: ident) => {
276        #[derive(Clone)]
277        pub struct $name<'a, A = ()>($ptr<Doc<'a, $name<'a, A>, A>>);
278
279        impl<'a, A> fmt::Debug for $name<'a, A>
280        where
281            A: fmt::Debug,
282        {
283            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
284                self.0.fmt(f)
285            }
286        }
287
288        impl<'a, A> $name<'a, A> {
289            pub fn new(doc: Doc<'a, $name<'a, A>, A>) -> $name<'a, A> {
290                $name($ptr::new(doc))
291            }
292        }
293
294        impl<'a, A> From<Doc<'a, Self, A>> for $name<'a, A> {
295            fn from(doc: Doc<'a, $name<'a, A>, A>) -> $name<'a, A> {
296                $name::new(doc)
297            }
298        }
299
300        impl<'a, A> Deref for $name<'a, A> {
301            type Target = Doc<'a, $name<'a, A>, A>;
302
303            fn deref(&self) -> &Self::Target {
304                &self.0
305            }
306        }
307
308        impl<'a, A> DocAllocator<'a, A> for $allocator
309        where
310            A: 'a,
311        {
312            type Doc = $name<'a, A>;
313
314            #[inline]
315            fn alloc(&'a self, doc: Doc<'a, Self::Doc, A>) -> Self::Doc {
316                $name::new(doc)
317            }
318            fn alloc_column_fn(
319                &'a self,
320                f: impl Fn(usize) -> Self::Doc + 'a,
321            ) -> <Self::Doc as DocPtr<'a, A>>::ColumnFn {
322                Rc::new(f)
323            }
324            fn alloc_width_fn(
325                &'a self,
326                f: impl Fn(isize) -> Self::Doc + 'a,
327            ) -> <Self::Doc as DocPtr<'a, A>>::WidthFn {
328                Rc::new(f)
329            }
330        }
331
332        impl<'a, A> DocPtr<'a, A> for $name<'a, A> {
333            type ColumnFn = std::rc::Rc<dyn Fn(usize) -> Self + 'a>;
334            type WidthFn = std::rc::Rc<dyn Fn(isize) -> Self + 'a>;
335        }
336
337        impl<'a, A> StaticDoc<'a, A> for $name<'a, A> {
338            type Allocator = $allocator;
339            const ALLOCATOR: &'static Self::Allocator = &$allocator;
340        }
341
342        impl_doc_methods!($name ('a, A) where () where ());
343
344        impl<'a, A> $name<'a, A> {
345            /// The text `t.to_string()`.
346            ///
347            /// The given text must not contain line breaks.
348            #[inline]
349            pub fn as_string<U: fmt::Display>(data: U) -> Self {
350                $allocator.as_string(data).into_doc()
351            }
352
353            /// The given text, which must not contain line breaks.
354            #[inline]
355            pub fn text<U: Into<Cow<'a, str>>>(data: U) -> Self {
356                $allocator.text(data).into_doc()
357            }
358
359            /// Append the given document after this document.
360            #[inline]
361            pub fn append<D>(self, that: D) -> Self
362            where
363                D: Pretty<'a, $allocator, A>,
364            {
365                DocBuilder(&$allocator, self.into()).append(that).into_doc()
366            }
367
368            /// A single document concatenating all the given documents.
369            #[inline]
370            pub fn concat<I>(docs: I) -> Self
371            where
372                I: IntoIterator,
373                I::Item: Pretty<'a, $allocator, A>,
374            {
375                $allocator.concat(docs).into_doc()
376            }
377
378            /// A single document interspersing the given separator `S` between the given documents.  For
379            /// example, if the documents are `[A, B, C, ..., Z]`, this yields `[A, S, B, S, C, S, ..., S, Z]`.
380            ///
381            /// Compare [the `intersperse` method from the `itertools` crate](https://docs.rs/itertools/0.5.9/itertools/trait.Itertools.html#method.intersperse).
382            ///
383            /// NOTE: The separator type, `S` may need to be cloned. Consider using cheaply cloneable ptr
384            /// like `RefDoc` or `RcDoc`
385            #[inline]
386            pub fn intersperse<I, S>(docs: I, separator: S) -> Self
387            where
388                I: IntoIterator,
389                I::Item: Pretty<'a, $allocator, A>,
390                S: Pretty<'a, $allocator, A> + Clone,
391                A: Clone,
392            {
393                $allocator.intersperse(docs, separator).into_doc()
394            }
395
396            /// Acts as `self` when laid out on multiple lines and acts as `that` when laid out on a single line.
397            #[inline]
398            pub fn flat_alt<D>(self, doc: D) -> Self
399            where
400                D: Pretty<'a, $allocator, A>,
401            {
402                DocBuilder(&$allocator, self.into())
403                    .flat_alt(doc)
404                    .into_doc()
405            }
406
407            /// Mark this document as a group.
408            ///
409            /// Groups are layed out on a single line if possible.  Within a group, all basic documents with
410            /// several possible layouts are assigned the same layout, that is, they are all layed out
411            /// horizontally and combined into a one single line, or they are each layed out on their own
412            /// line.
413            #[inline]
414            pub fn group(self) -> Self {
415                DocBuilder(&$allocator, self.into()).group().into_doc()
416            }
417
418            /// Increase the indentation level of this document.
419            #[inline]
420            pub fn nest(self, offset: isize) -> Self {
421                DocBuilder(&$allocator, self.into()).nest(offset).into_doc()
422            }
423
424            #[inline]
425            pub fn annotate(self, ann: A) -> Self {
426                DocBuilder(&$allocator, self.into())
427                    .annotate(ann)
428                    .into_doc()
429            }
430
431            #[inline]
432            pub fn union<D>(self, other: D) -> Self
433            where
434                D: Into<BuildDoc<'a, Self, A>>,
435            {
436                DocBuilder(&$allocator, self.into()).union(other).into_doc()
437            }
438
439            #[inline]
440            pub fn softline() -> Self {
441                Self::line().group()
442            }
443
444            /// A `softline_` acts like `nil` if the document fits the page, otherwise like `line_`
445            #[inline]
446            pub fn softline_() -> Self {
447                Self::line_().group()
448            }
449
450            #[inline]
451            pub fn column(f: impl Fn(usize) -> Self + 'static) -> Self {
452                DocBuilder(&$allocator, Doc::Column($allocator.alloc_column_fn(f)).into()).into_doc()
453            }
454
455            #[inline]
456            pub fn nesting(f: impl Fn(usize) -> Self + 'static) -> Self {
457                DocBuilder(&$allocator, Doc::Nesting($allocator.alloc_column_fn(f)).into()).into_doc()
458            }
459        }
460    };
461}
462
463enum FmtText {
464    Small(SmallText),
465    Large(String),
466}
467
468impl fmt::Write for FmtText {
469    fn write_str(&mut self, s: &str) -> fmt::Result {
470        match self {
471            FmtText::Small(buf) => {
472                if buf.try_push_str(s).is_err() {
473                    let mut new_str = String::with_capacity(buf.len() + s.len());
474                    new_str.push_str(buf);
475                    new_str.push_str(s);
476                    *self = FmtText::Large(new_str);
477                }
478            }
479            FmtText::Large(buf) => buf.push_str(s),
480        }
481        Ok(())
482    }
483}
484
485macro_rules! impl_doc_methods {
486    ($name: ident ( $($params: tt)* ) where ( $($where_: tt)* ) where ( $($where_2: tt)* )) => {
487        impl< $($params)* > $name< $($params)* >
488            where $($where_)*
489        {
490            /// An empty document.
491            #[inline]
492            pub fn nil() -> Self {
493                Doc::Nil.into()
494            }
495
496            /// A single hardline.
497            #[inline]
498            pub fn hardline() -> Self {
499                Doc::Hardline.into()
500            }
501
502            #[inline]
503            pub fn space() -> Self {
504                Doc::BorrowedText(" ").into()
505            }
506
507            #[inline]
508            pub fn fail() -> Self {
509                Doc::Fail.into()
510            }
511        }
512
513        impl< $($params)* > $name< $($params)* >
514            where $($where_2)*
515        {
516            /// A line acts like a `\n` but behaves like `space` if it is grouped on a single line.
517            #[inline]
518            pub fn line() -> Self {
519                Self::hardline().flat_alt(Self::space()).into()
520            }
521
522            /// Acts like `line` but behaves like `nil` if grouped on a single line
523            #[inline]
524            pub fn line_() -> Self {
525                Self::hardline().flat_alt(Self::nil()).into()
526            }
527        }
528    };
529}
530
531impl_doc!(BoxDoc, Box, BoxAllocator);
532impl_doc!(RcDoc, Rc, RcAllocator);
533
534impl_doc_methods!(Doc ('a, D, A) where (D: DocPtr<'a, A>) where (D: StaticDoc<'a, A>));
535impl_doc_methods!(BuildDoc ('a, D, A) where (D: DocPtr<'a, A>) where (D: StaticDoc<'a, A>));
536
537pub struct BoxAllocator;
538
539pub struct RcAllocator;
540
541impl<'a, T, A> BuildDoc<'a, T, A>
542where
543    T: StaticDoc<'a, A>,
544{
545    /// The text `t.to_string()`.
546    ///
547    /// The given text must not contain line breaks.
548    #[inline]
549    pub fn as_string<U: fmt::Display>(data: U) -> Self {
550        T::ALLOCATOR.as_string(data).1
551    }
552
553    /// The given text, which must not contain line breaks.
554    #[inline]
555    pub fn text<U: Into<Cow<'a, str>>>(data: U) -> Self {
556        T::ALLOCATOR.text(data).1
557    }
558
559    fn flat_alt<D>(self, doc: D) -> Self
560    where
561        D: Pretty<'a, T::Allocator, A>,
562    {
563        DocBuilder(T::ALLOCATOR, self).flat_alt(doc).1
564    }
565}
566
567impl<'a, T, A> Doc<'a, T, A>
568where
569    T: StaticDoc<'a, A>,
570{
571    /// The text `t.to_string()`.
572    ///
573    /// The given text must not contain line breaks.
574    #[inline]
575    pub fn as_string<U: fmt::Display>(data: U) -> Self {
576        T::ALLOCATOR.as_string(data).into_plain_doc()
577    }
578
579    /// The given text, which must not contain line breaks.
580    #[inline]
581    pub fn text<U: Into<Cow<'a, str>>>(data: U) -> Self {
582        T::ALLOCATOR.text(data).into_plain_doc()
583    }
584
585    fn flat_alt<D>(self, doc: D) -> Self
586    where
587        D: Pretty<'a, T::Allocator, A>,
588    {
589        DocBuilder(T::ALLOCATOR, self.into())
590            .flat_alt(doc)
591            .into_plain_doc()
592    }
593}
594
595pub trait StaticDoc<'a, A>: DocPtr<'a, A>
596where
597    A: 'a,
598{
599    type Allocator: DocAllocator<'a, A, Doc = Self> + 'static;
600    const ALLOCATOR: &'static Self::Allocator;
601}
602
603impl<'a, T, A, S> From<S> for Doc<'a, T, A>
604where
605    T: StaticDoc<'a, A>,
606    S: Into<Cow<'a, str>>,
607{
608    fn from(s: S) -> Doc<'a, T, A> {
609        Doc::text(s)
610    }
611}
612
613pub struct PrettyFmt<'a, 'd, T, A>
614where
615    A: 'a,
616    T: DocPtr<'a, A> + 'a,
617{
618    doc: &'d Doc<'a, T, A>,
619    width: usize,
620}
621
622impl<'a, T, A> fmt::Display for PrettyFmt<'a, '_, T, A>
623where
624    T: DocPtr<'a, A>,
625{
626    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
627        self.doc.render_fmt(self.width, f)
628    }
629}
630
631impl<'a, T, A> Doc<'a, T, A>
632where
633    T: DocPtr<'a, A> + 'a,
634{
635    /// Writes a rendered document to a `std::io::Write` object.
636    #[inline]
637    pub fn render<W>(&self, width: usize, out: &mut W) -> io::Result<()>
638    where
639        W: ?Sized + io::Write,
640    {
641        self.render_raw(width, &mut IoWrite::new(out))
642    }
643
644    /// Writes a rendered document to a `std::fmt::Write` object.
645    #[inline]
646    pub fn render_fmt<W>(&self, width: usize, out: &mut W) -> fmt::Result
647    where
648        W: ?Sized + fmt::Write,
649    {
650        self.render_raw(width, &mut FmtWrite::new(out))
651    }
652
653    /// Writes a rendered document to a `RenderAnnotated<A>` object.
654    #[inline]
655    pub fn render_raw<W>(&self, width: usize, out: &mut W) -> Result<(), W::Error>
656    where
657        for<'b> W: render::RenderAnnotated<'b, A>,
658        W: ?Sized,
659    {
660        render::best(self, width, out)
661    }
662
663    /// Returns a value which implements `std::fmt::Display`
664    ///
665    /// ```
666    /// use pretty::{Doc, BoxDoc};
667    /// let doc = BoxDoc::<()>::group(
668    ///     BoxDoc::text("hello").append(Doc::line()).append(Doc::text("world"))
669    /// );
670    /// assert_eq!(format!("{}", doc.pretty(80)), "hello world");
671    /// ```
672    #[inline]
673    pub fn pretty<'d>(&'d self, width: usize) -> PrettyFmt<'a, 'd, T, A> {
674        PrettyFmt { doc: self, width }
675    }
676}
677
678#[cfg(feature = "termcolor")]
679impl<'a, T> Doc<'a, T, ColorSpec>
680where
681    T: DocPtr<'a, ColorSpec> + 'a,
682{
683    #[inline]
684    pub fn render_colored<W>(&self, width: usize, out: W) -> io::Result<()>
685    where
686        W: WriteColor,
687    {
688        render::best(self, width, &mut TermColored::new(out))
689    }
690}
691
692/// The `DocBuilder` type allows for convenient appending of documents even for arena allocated
693/// documents by storing the arena inline.
694pub struct DocBuilder<'a, D, A = ()>(pub &'a D, pub BuildDoc<'a, D::Doc, A>)
695where
696    D: ?Sized + DocAllocator<'a, A>;
697
698impl<'a, D, A, P> Add<P> for DocBuilder<'a, D, A>
699where
700    D: ?Sized + DocAllocator<'a, A>,
701    P: Pretty<'a, D, A>,
702{
703    type Output = DocBuilder<'a, D, A>;
704    fn add(self, other: P) -> Self::Output {
705        self.append(other)
706    }
707}
708
709impl<'a, D, A, P> AddAssign<P> for DocBuilder<'a, D, A>
710where
711    D: ?Sized + DocAllocator<'a, A>,
712    P: Pretty<'a, D, A>,
713{
714    fn add_assign(&mut self, other: P) {
715        *self = DocBuilder(self.0, std::mem::take(&mut self.1)).append(other)
716    }
717}
718
719impl<'a, D, A> Deref for DocBuilder<'a, D, A>
720where
721    D: ?Sized + DocAllocator<'a, A>,
722{
723    type Target = Doc<'a, D::Doc, A>;
724    fn deref(&self) -> &Self::Target {
725        match &self.1 {
726            BuildDoc::DocPtr(d) => d,
727            BuildDoc::Doc(d) => d,
728        }
729    }
730}
731
732impl<'a, D, A> fmt::Debug for DocBuilder<'a, D, A>
733where
734    D: ?Sized + DocAllocator<'a, A>,
735    D::Doc: fmt::Debug,
736    A: fmt::Debug,
737{
738    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
739        self.1.fmt(f)
740    }
741}
742
743impl<'a, A, D> Clone for DocBuilder<'a, D, A>
744where
745    A: Clone,
746    D: DocAllocator<'a, A> + 'a,
747    D::Doc: Clone,
748{
749    fn clone(&self) -> Self {
750        DocBuilder(self.0, self.1.clone())
751    }
752}
753
754impl<'a, D, A> From<DocBuilder<'a, D, A>> for BuildDoc<'a, D::Doc, A>
755where
756    D: ?Sized + DocAllocator<'a, A>,
757{
758    fn from(val: DocBuilder<'a, D, A>) -> Self {
759        val.1
760    }
761}
762
763pub trait DocPtr<'a, A>: Deref<Target = Doc<'a, Self, A>> + Sized
764where
765    A: 'a,
766{
767    type ColumnFn: Deref<Target = dyn Fn(usize) -> Self + 'a> + Clone + 'a;
768    type WidthFn: Deref<Target = dyn Fn(isize) -> Self + 'a> + Clone + 'a;
769}
770
771impl<'a, A> DocPtr<'a, A> for RefDoc<'a, A> {
772    type ColumnFn = &'a (dyn Fn(usize) -> Self + 'a);
773    type WidthFn = &'a (dyn Fn(isize) -> Self + 'a);
774}
775
776/// Trait for types which can be converted to a `Document`
777pub trait Pretty<'a, D, A = ()>
778where
779    A: 'a,
780    D: ?Sized + DocAllocator<'a, A>,
781{
782    /// Converts `self` into a document
783    fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A>;
784}
785
786impl<'a, A> Pretty<'a, BoxAllocator, A> for BoxDoc<'a, A>
787where
788    A: 'a,
789{
790    fn pretty(self, allocator: &'a BoxAllocator) -> DocBuilder<'a, BoxAllocator, A> {
791        DocBuilder(allocator, self.into())
792    }
793}
794
795impl<'a, A> Pretty<'a, RcAllocator, A> for RcDoc<'a, A>
796where
797    A: 'a,
798{
799    fn pretty(self, allocator: &'a RcAllocator) -> DocBuilder<'a, RcAllocator, A> {
800        DocBuilder(allocator, self.into())
801    }
802}
803
804impl<'a, A> Pretty<'a, Arena<'a, A>, A> for RefDoc<'a, A>
805where
806    A: 'a,
807{
808    fn pretty(self, allocator: &'a Arena<'a, A>) -> DocBuilder<'a, Arena<'a, A>, A> {
809        DocBuilder(allocator, self.into())
810    }
811}
812
813impl<'a, D, A> Pretty<'a, D, A> for BuildDoc<'a, D::Doc, A>
814where
815    A: 'a,
816    D: ?Sized + DocAllocator<'a, A>,
817{
818    fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> {
819        DocBuilder(allocator, self)
820    }
821}
822
823impl<'a, D, A> Pretty<'a, D, A> for Doc<'a, D::Doc, A>
824where
825    A: 'a,
826    D: ?Sized + DocAllocator<'a, A>,
827{
828    fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> {
829        DocBuilder(allocator, self.into())
830    }
831}
832
833impl<'a, D, A> Pretty<'a, D, A> for DocBuilder<'a, D, A>
834where
835    A: 'a,
836    D: ?Sized + DocAllocator<'a, A>,
837{
838    fn pretty(self, _: &'a D) -> DocBuilder<'a, D, A> {
839        self
840    }
841}
842
843impl<'a, D, A, T> Pretty<'a, D, A> for Option<T>
844where
845    A: 'a,
846    D: ?Sized + DocAllocator<'a, A>,
847    T: Pretty<'a, D, A>,
848{
849    fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> {
850        match self {
851            Some(x) => x.pretty(allocator),
852            None => allocator.nil(),
853        }
854    }
855}
856
857impl<'a, D, A> Pretty<'a, D, A> for &'a str
858where
859    A: 'a,
860    D: ?Sized + DocAllocator<'a, A>,
861{
862    fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> {
863        allocator.text(self)
864    }
865}
866
867impl<'a, D, A> Pretty<'a, D, A> for &'a String
868where
869    A: 'a,
870    D: ?Sized + DocAllocator<'a, A>,
871{
872    fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> {
873        self[..].pretty(allocator)
874    }
875}
876
877impl<'a, D, A> Pretty<'a, D, A> for String
878where
879    A: 'a,
880    D: ?Sized + DocAllocator<'a, A>,
881{
882    fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> {
883        allocator.text(self)
884    }
885}
886
887impl<'a, D, A, S> Pretty<'a, D, A> for Cow<'a, S>
888where
889    A: 'a,
890    D: ?Sized + DocAllocator<'a, A>,
891    S: ?Sized + ToOwned,
892    &'a S: Pretty<'a, D, A>,
893    S::Owned: Pretty<'a, D, A>,
894{
895    fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> {
896        match self {
897            Cow::Borrowed(s) => s.pretty(allocator),
898            Cow::Owned(s) => s.pretty(allocator),
899        }
900    }
901}
902
903/// The `DocAllocator` trait abstracts over a type which can allocate (pointers to) `Doc`.
904pub trait DocAllocator<'a, A = ()>
905where
906    A: 'a,
907{
908    type Doc: DocPtr<'a, A>;
909
910    fn alloc(&'a self, doc: Doc<'a, Self::Doc, A>) -> Self::Doc;
911
912    fn alloc_column_fn(
913        &'a self,
914        f: impl Fn(usize) -> Self::Doc + 'a,
915    ) -> <Self::Doc as DocPtr<'a, A>>::ColumnFn;
916
917    fn alloc_width_fn(
918        &'a self,
919        f: impl Fn(isize) -> Self::Doc + 'a,
920    ) -> <Self::Doc as DocPtr<'a, A>>::WidthFn;
921
922    fn alloc_cow(&'a self, doc: BuildDoc<'a, Self::Doc, A>) -> Self::Doc {
923        match doc {
924            BuildDoc::DocPtr(d) => d,
925            BuildDoc::Doc(d) => self.alloc(d),
926        }
927    }
928
929    /// Allocate an empty document.
930    #[inline]
931    fn nil(&'a self) -> DocBuilder<'a, Self, A> {
932        DocBuilder(self, Doc::Nil.into())
933    }
934
935    /// Fails document rendering immediately.
936    ///
937    /// Primarily used to abort rendering inside the left side of `Union`
938    #[inline]
939    fn fail(&'a self) -> DocBuilder<'a, Self, A> {
940        DocBuilder(self, Doc::Fail.into())
941    }
942
943    /// Allocate a single hardline.
944    #[inline]
945    fn hardline(&'a self) -> DocBuilder<'a, Self, A> {
946        DocBuilder(self, Doc::Hardline.into())
947    }
948
949    #[inline]
950    fn space(&'a self) -> DocBuilder<'a, Self, A> {
951        self.text(" ")
952    }
953
954    /// A line acts like a `\n` but behaves like `space` if it is grouped on a single line.
955    #[inline]
956    fn line(&'a self) -> DocBuilder<'a, Self, A> {
957        self.hardline().flat_alt(self.space())
958    }
959
960    /// Acts like `line` but behaves like `nil` if grouped on a single line
961    ///
962    /// ```
963    /// use pretty::{Doc, RcDoc};
964    ///
965    /// let doc = RcDoc::<()>::group(
966    ///     RcDoc::text("(")
967    ///         .append(
968    ///             RcDoc::line_()
969    ///                 .append(Doc::text("test"))
970    ///                 .append(Doc::line())
971    ///                 .append(Doc::text("test"))
972    ///                 .nest(2),
973    ///         )
974    ///         .append(Doc::line_())
975    ///         .append(Doc::text(")")),
976    /// );
977    /// assert_eq!(doc.pretty(5).to_string(), "(\n  test\n  test\n)");
978    /// assert_eq!(doc.pretty(100).to_string(), "(test test)");
979    /// ```
980    #[inline]
981    fn line_(&'a self) -> DocBuilder<'a, Self, A> {
982        self.hardline().flat_alt(self.nil())
983    }
984
985    /// A `softline` acts like `space` if the document fits the page, otherwise like `line`
986    #[inline]
987    fn softline(&'a self) -> DocBuilder<'a, Self, A> {
988        self.line().group()
989    }
990
991    /// A `softline_` acts like `nil` if the document fits the page, otherwise like `line_`
992    #[inline]
993    fn softline_(&'a self) -> DocBuilder<'a, Self, A> {
994        self.line_().group()
995    }
996
997    /// Allocate a document containing the text `t.to_string()`.
998    ///
999    /// The given text must not contain line breaks.
1000    #[inline]
1001    fn as_string<U: fmt::Display>(&'a self, data: U) -> DocBuilder<'a, Self, A> {
1002        use std::fmt::Write;
1003        let mut buf = FmtText::Small(SmallText::new());
1004        write!(buf, "{}", data).unwrap();
1005        let doc = match buf {
1006            FmtText::Small(b) => Doc::SmallText(b),
1007            FmtText::Large(b) => Doc::OwnedText(b.into()),
1008        };
1009        DocBuilder(self, doc.into()).with_utf8_len()
1010    }
1011
1012    /// Allocate a document containing the given text.
1013    ///
1014    /// The given text must not contain line breaks.
1015    #[inline]
1016    fn text<U: Into<Cow<'a, str>>>(&'a self, data: U) -> DocBuilder<'a, Self, A> {
1017        let data: Cow<_> = data.into();
1018        let doc = if data.is_empty() {
1019            Doc::Nil.into()
1020        } else {
1021            match data {
1022                Cow::Owned(t) => Doc::OwnedText(t.into()).into(),
1023                Cow::Borrowed(t) => Doc::BorrowedText(t).into(),
1024            }
1025        };
1026        DocBuilder(self, doc).with_utf8_len()
1027    }
1028
1029    /// Allocate a document concatenating the given documents.
1030    #[inline]
1031    fn concat<I>(&'a self, docs: I) -> DocBuilder<'a, Self, A>
1032    where
1033        I: IntoIterator,
1034        I::Item: Pretty<'a, Self, A>,
1035    {
1036        docs.into_iter().fold(self.nil(), |a, b| a.append(b))
1037    }
1038
1039    /// Allocate a document that intersperses the given separator `S` between the given documents
1040    /// `[A, B, C, ..., Z]`, yielding `[A, S, B, S, C, S, ..., S, Z]`.
1041    ///
1042    /// Compare [the `intersperse` method from the `itertools` crate](https://docs.rs/itertools/0.5.9/itertools/trait.Itertools.html#method.intersperse).
1043    ///
1044    /// NOTE: The separator type, `S` may need to be cloned. Consider using cheaply cloneable ptr
1045    /// like `RefDoc` or `RcDoc`
1046    #[inline]
1047    fn intersperse<I, S>(&'a self, docs: I, separator: S) -> DocBuilder<'a, Self, A>
1048    where
1049        I: IntoIterator,
1050        I::Item: Pretty<'a, Self, A>,
1051        S: Pretty<'a, Self, A> + Clone,
1052    {
1053        let mut result = self.nil();
1054        let mut iter = docs.into_iter();
1055
1056        if let Some(first) = iter.next() {
1057            result = result.append(first);
1058
1059            for doc in iter {
1060                result = result.append(separator.clone());
1061                result = result.append(doc);
1062            }
1063        }
1064
1065        result
1066    }
1067
1068    /// Allocate a document that acts differently based on the position and page layout
1069    ///
1070    /// ```rust
1071    /// use pretty::DocAllocator;
1072    ///
1073    /// let arena = pretty::Arena::<()>::new();
1074    /// let doc = arena.text("prefix ")
1075    ///     .append(arena.column(|l| {
1076    ///         arena.text("| <- column ").append(arena.as_string(l)).into_doc()
1077    ///     }));
1078    /// assert_eq!(doc.1.pretty(80).to_string(), "prefix | <- column 7");
1079    /// ```
1080    #[inline]
1081    fn column(&'a self, f: impl Fn(usize) -> Self::Doc + 'a) -> DocBuilder<'a, Self, A> {
1082        DocBuilder(self, Doc::Column(self.alloc_column_fn(f)).into())
1083    }
1084
1085    /// Allocate a document that acts differently based on the current nesting level
1086    ///
1087    /// ```rust
1088    /// use pretty::DocAllocator;
1089    ///
1090    /// let arena = pretty::Arena::<()>::new();
1091    /// let doc = arena.text("prefix ")
1092    ///     .append(arena.nesting(|l| {
1093    ///         arena.text("[Nested: ").append(arena.as_string(l)).append("]").into_doc()
1094    ///     }).nest(4));
1095    /// assert_eq!(doc.1.pretty(80).to_string(), "prefix [Nested: 4]");
1096    /// ```
1097    #[inline]
1098    fn nesting(&'a self, f: impl Fn(usize) -> Self::Doc + 'a) -> DocBuilder<'a, Self, A> {
1099        DocBuilder(self, Doc::Nesting(self.alloc_column_fn(f)).into())
1100    }
1101
1102    /// Reflows `text` inserting `softline` in place of any whitespace
1103    #[inline]
1104    fn reflow(&'a self, text: &'a str) -> DocBuilder<'a, Self, A>
1105    where
1106        Self: Sized,
1107        Self::Doc: Clone,
1108        A: Clone,
1109    {
1110        self.intersperse(text.split(char::is_whitespace), self.softline())
1111    }
1112}
1113
1114/// Either a `Doc` or a pointer to a `Doc` (`D`)
1115#[derive(Clone)]
1116pub enum BuildDoc<'a, D, A>
1117where
1118    D: DocPtr<'a, A>,
1119{
1120    DocPtr(D),
1121    Doc(Doc<'a, D, A>),
1122}
1123
1124impl<'a, D, A> Default for BuildDoc<'a, D, A>
1125where
1126    D: DocPtr<'a, A>,
1127{
1128    fn default() -> Self {
1129        Self::Doc(Doc::default())
1130    }
1131}
1132
1133impl<'a, D, A> fmt::Debug for BuildDoc<'a, D, A>
1134where
1135    D: DocPtr<'a, A> + fmt::Debug,
1136    A: fmt::Debug,
1137{
1138    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1139        (**self).fmt(f)
1140    }
1141}
1142
1143impl<'a, D, A> Deref for BuildDoc<'a, D, A>
1144where
1145    D: DocPtr<'a, A>,
1146{
1147    type Target = Doc<'a, D, A>;
1148    fn deref(&self) -> &Self::Target {
1149        match self {
1150            BuildDoc::DocPtr(d) => d,
1151            BuildDoc::Doc(d) => d,
1152        }
1153    }
1154}
1155
1156impl<'a, A> From<RefDoc<'a, A>> for BuildDoc<'a, RefDoc<'a, A>, A> {
1157    fn from(s: RefDoc<'a, A>) -> Self {
1158        BuildDoc::DocPtr(s)
1159    }
1160}
1161
1162impl<'a, A> From<BoxDoc<'a, A>> for BuildDoc<'a, BoxDoc<'a, A>, A> {
1163    fn from(s: BoxDoc<'a, A>) -> Self {
1164        BuildDoc::DocPtr(s)
1165    }
1166}
1167
1168impl<'a, A> From<RcDoc<'a, A>> for BuildDoc<'a, RcDoc<'a, A>, A> {
1169    fn from(s: RcDoc<'a, A>) -> Self {
1170        BuildDoc::DocPtr(s)
1171    }
1172}
1173
1174impl<'a, T, A> From<Doc<'a, T, A>> for BuildDoc<'a, T, A>
1175where
1176    T: DocPtr<'a, A>,
1177{
1178    fn from(s: Doc<'a, T, A>) -> Self {
1179        BuildDoc::Doc(s)
1180    }
1181}
1182
1183impl<'a, T, A> From<String> for BuildDoc<'a, T, A>
1184where
1185    T: StaticDoc<'a, A>,
1186{
1187    fn from(s: String) -> Self {
1188        BuildDoc::Doc(Doc::text(s))
1189    }
1190}
1191
1192impl<'a, T, A> From<&'a str> for BuildDoc<'a, T, A>
1193where
1194    T: StaticDoc<'a, A>,
1195{
1196    fn from(s: &'a str) -> Self {
1197        BuildDoc::Doc(Doc::text(s))
1198    }
1199}
1200
1201impl<'a, T, A> From<&'a String> for BuildDoc<'a, T, A>
1202where
1203    T: StaticDoc<'a, A>,
1204{
1205    fn from(s: &'a String) -> Self {
1206        BuildDoc::Doc(Doc::text(s))
1207    }
1208}
1209
1210impl<'a, T, A, S> From<Option<S>> for BuildDoc<'a, T, A>
1211where
1212    T: DocPtr<'a, A>,
1213    S: Into<BuildDoc<'a, T, A>>,
1214{
1215    fn from(s: Option<S>) -> Self {
1216        match s {
1217            Some(s) => s.into(),
1218            None => BuildDoc::Doc(Doc::Nil),
1219        }
1220    }
1221}
1222
1223/// Concatenates a number of documents (or values that can be converted into a document via the
1224/// `Pretty` trait, like `&str`)
1225///
1226/// ```
1227/// use pretty::{docs, Arena, DocAllocator};
1228/// let arena = &Arena::<()>::new();
1229/// let doc = docs![
1230///     arena,
1231///     "let",
1232///     arena.softline(),
1233///     "x",
1234///     arena.softline(),
1235///     "=",
1236///     arena.softline(),
1237///     Some("123"),
1238/// ];
1239/// assert_eq!(doc.1.pretty(80).to_string(), "let x = 123");
1240/// ```
1241#[macro_export]
1242macro_rules! docs {
1243    ($alloc: expr, $first: expr $(,)?) => {
1244        $crate::Pretty::pretty($first, $alloc)
1245    };
1246    ($alloc: expr, $first: expr $(, $rest: expr)+ $(,)?) => {{
1247        let mut doc = $crate::Pretty::pretty($first, $alloc);
1248        $(
1249            doc = doc.append($rest);
1250        )*
1251        doc
1252    }}
1253}
1254
1255impl<'a, D, A> DocBuilder<'a, D, A>
1256where
1257    A: 'a,
1258    D: ?Sized + DocAllocator<'a, A>,
1259{
1260    fn with_utf8_len(self) -> Self {
1261        let s = match &*self {
1262            Doc::OwnedText(s) => &s[..],
1263            Doc::BorrowedText(s) => s,
1264            Doc::SmallText(s) => s,
1265            _ => return self,
1266        };
1267
1268        if s.is_ascii() {
1269            self
1270        } else {
1271            let display_width = unicode_width::UnicodeWidthStr::width(s);
1272
1273            let DocBuilder(allocator, _) = self;
1274            DocBuilder(
1275                allocator,
1276                Doc::RenderLen(display_width, self.into_doc()).into(),
1277            )
1278        }
1279    }
1280
1281    /// Append the given document after this document.
1282    #[inline]
1283    pub fn append<E>(self, that: E) -> DocBuilder<'a, D, A>
1284    where
1285        E: Pretty<'a, D, A>,
1286    {
1287        let DocBuilder(allocator, _) = self;
1288        let that = that.pretty(allocator);
1289        match (&*self, &*that) {
1290            (Doc::Nil, _) => that,
1291            (_, Doc::Nil) => self,
1292            _ => DocBuilder(
1293                allocator,
1294                Doc::Append(
1295                    allocator.alloc_cow(self.into()),
1296                    allocator.alloc_cow(that.into()),
1297                )
1298                .into(),
1299            ),
1300        }
1301    }
1302
1303    /// Acts as `self` when laid out on multiple lines and acts as `that` when laid out on a single line.
1304    ///
1305    /// ```
1306    /// use pretty::{Arena, DocAllocator};
1307    ///
1308    /// let arena = Arena::<()>::new();
1309    /// let body = arena.line().append("x");
1310    /// let doc = arena.text("let")
1311    ///     .append(arena.line())
1312    ///     .append("x")
1313    ///     .group()
1314    ///     .append(
1315    ///         body.clone()
1316    ///             .flat_alt(
1317    ///                 arena.line()
1318    ///                     .append("in")
1319    ///                     .append(body)
1320    ///             )
1321    ///     )
1322    ///     .group();
1323    ///
1324    /// assert_eq!(doc.1.pretty(100).to_string(), "let x in x");
1325    /// assert_eq!(doc.1.pretty(8).to_string(), "let x\nx");
1326    /// ```
1327    #[inline]
1328    pub fn flat_alt<E>(self, that: E) -> DocBuilder<'a, D, A>
1329    where
1330        E: Pretty<'a, D, A>,
1331    {
1332        let DocBuilder(allocator, this) = self;
1333        let that = that.pretty(allocator);
1334        DocBuilder(
1335            allocator,
1336            Doc::FlatAlt(allocator.alloc_cow(this), allocator.alloc_cow(that.into())).into(),
1337        )
1338    }
1339
1340    /// Mark this document as a group.
1341    ///
1342    /// Groups are layed out on a single line if possible.  Within a group, all basic documents with
1343    /// several possible layouts are assigned the same layout, that is, they are all layed out
1344    /// horizontally and combined into a one single line, or they are each layed out on their own
1345    /// line.
1346    #[inline]
1347    pub fn group(self) -> DocBuilder<'a, D, A> {
1348        match *self.1 {
1349            Doc::Group(_)
1350            | Doc::OwnedText(_)
1351            | Doc::BorrowedText(_)
1352            | Doc::SmallText(_)
1353            | Doc::Nil => self,
1354            _ => {
1355                let DocBuilder(allocator, this) = self;
1356                DocBuilder(allocator, Doc::Group(allocator.alloc_cow(this)).into())
1357            }
1358        }
1359    }
1360
1361    /// Increase the indentation level of this document.
1362    #[inline]
1363    pub fn nest(self, offset: isize) -> DocBuilder<'a, D, A> {
1364        if let Doc::Nil = &*self.1 {
1365            return self;
1366        }
1367        if offset == 0 {
1368            return self;
1369        }
1370        let DocBuilder(allocator, this) = self;
1371        DocBuilder(
1372            allocator,
1373            Doc::Nest(offset, allocator.alloc_cow(this)).into(),
1374        )
1375    }
1376
1377    #[inline]
1378    pub fn annotate(self, ann: A) -> DocBuilder<'a, D, A> {
1379        let DocBuilder(allocator, this) = self;
1380        DocBuilder(
1381            allocator,
1382            Doc::Annotated(ann, allocator.alloc_cow(this)).into(),
1383        )
1384    }
1385
1386    #[inline]
1387    pub fn union<E>(self, other: E) -> DocBuilder<'a, D, A>
1388    where
1389        E: Into<BuildDoc<'a, D::Doc, A>>,
1390    {
1391        let DocBuilder(allocator, this) = self;
1392        let other = other.into();
1393        let doc = Doc::Union(allocator.alloc_cow(this), allocator.alloc_cow(other));
1394        DocBuilder(allocator, doc.into())
1395    }
1396
1397    /// Lays out `self` so with the nesting level set to the current column
1398    ///
1399    /// NOTE: The doc pointer type, `D` may need to be cloned. Consider using cheaply cloneable ptr
1400    /// like `RefDoc` or `RcDoc`
1401    ///
1402    /// ```rust
1403    /// use pretty::{docs, DocAllocator};
1404    ///
1405    /// let arena = &pretty::Arena::<()>::new();
1406    /// let doc = docs![
1407    ///     arena,
1408    ///     "lorem",
1409    ///     " ",
1410    ///     arena.intersperse(["ipsum", "dolor"].iter().cloned(), arena.line_()).align(),
1411    ///     arena.hardline(),
1412    ///     "next",
1413    /// ];
1414    /// assert_eq!(doc.1.pretty(80).to_string(), "lorem ipsum\n      dolor\nnext");
1415    /// ```
1416    #[inline]
1417    pub fn align(self) -> DocBuilder<'a, D, A>
1418    where
1419        DocBuilder<'a, D, A>: Clone,
1420    {
1421        let allocator = self.0;
1422        allocator.column(move |col| {
1423            let self_ = self.clone();
1424            allocator
1425                .nesting(move |nest| self_.clone().nest(col as isize - nest as isize).into_doc())
1426                .into_doc()
1427        })
1428    }
1429
1430    /// Lays out `self` with a nesting level set to the current level plus `adjust`.
1431    ///
1432    /// NOTE: The doc pointer type, `D` may need to be cloned. Consider using cheaply cloneable ptr
1433    /// like `RefDoc` or `RcDoc`
1434    ///
1435    /// ```rust
1436    /// use pretty::DocAllocator;
1437    ///
1438    /// let arena = pretty::Arena::<()>::new();
1439    /// let doc = arena.text("prefix").append(arena.text(" "))
1440    ///     .append(arena.reflow("Indenting these words with nest").hang(4));
1441    /// assert_eq!(
1442    ///     doc.1.pretty(24).to_string(),
1443    ///     "prefix Indenting these\n           words with\n           nest",
1444    /// );
1445    /// ```
1446    #[inline]
1447    pub fn hang(self, adjust: isize) -> DocBuilder<'a, D, A>
1448    where
1449        DocBuilder<'a, D, A>: Clone,
1450    {
1451        self.nest(adjust).align()
1452    }
1453
1454    /// Indents `self` by `adjust` spaces from the current cursor position
1455    ///
1456    /// NOTE: The doc pointer type, `D` may need to be cloned. Consider using cheaply cloneable ptr
1457    /// like `RefDoc` or `RcDoc`
1458    ///
1459    /// ```rust
1460    /// use pretty::DocAllocator;
1461    ///
1462    /// let arena = pretty::Arena::<()>::new();
1463    /// let doc = arena.text("prefix").append(arena.text(" "))
1464    ///     .append(arena.reflow("The indent function indents these words!").indent(4));
1465    /// assert_eq!(
1466    ///     doc.1.pretty(24).to_string(),
1467    /// "
1468    /// prefix     The indent
1469    ///            function
1470    ///            indents these
1471    ///            words!".trim_start(),
1472    /// );
1473    /// ```
1474    #[inline]
1475    pub fn indent(self, adjust: usize) -> DocBuilder<'a, D, A>
1476    where
1477        DocBuilder<'a, D, A>: Clone,
1478    {
1479        let spaces = {
1480            use crate::render::SPACES;
1481            let DocBuilder(allocator, _) = self;
1482            let mut doc = allocator.nil();
1483            let mut remaining = adjust;
1484            while remaining != 0 {
1485                let i = SPACES.len().min(remaining);
1486                remaining -= i;
1487                doc = doc.append(allocator.text(&SPACES[..i]))
1488            }
1489            doc
1490        };
1491        spaces.append(self).hang(adjust.try_into().unwrap())
1492    }
1493
1494    /// Lays out `self` and provides the column width of it available to `f`
1495    ///
1496    /// NOTE: The doc pointer type, `D` may need to be cloned. Consider using cheaply cloneable ptr
1497    /// like `RefDoc` or `RcDoc`
1498    ///
1499    /// ```rust
1500    /// use pretty::DocAllocator;
1501    ///
1502    /// let arena = pretty::Arena::<()>::new();
1503    /// let doc = arena.text("prefix ")
1504    ///     .append(arena.column(|l| {
1505    ///         arena.text("| <- column ").append(arena.as_string(l)).into_doc()
1506    ///     }));
1507    /// assert_eq!(doc.1.pretty(80).to_string(), "prefix | <- column 7");
1508    /// ```
1509    #[inline]
1510    pub fn width(self, f: impl Fn(isize) -> D::Doc + 'a) -> DocBuilder<'a, D, A>
1511    where
1512        BuildDoc<'a, D::Doc, A>: Clone,
1513    {
1514        let DocBuilder(allocator, this) = self;
1515        let f = allocator.alloc_width_fn(f);
1516        allocator.column(move |start| {
1517            let f = f.clone();
1518
1519            DocBuilder(allocator, this.clone())
1520                .append(allocator.column(move |end| f(end as isize - start as isize)))
1521                .into_doc()
1522        })
1523    }
1524
1525    /// Puts `self` between `before` and `after`
1526    #[inline]
1527    pub fn enclose<E, F>(self, before: E, after: F) -> DocBuilder<'a, D, A>
1528    where
1529        E: Pretty<'a, D, A>,
1530        F: Pretty<'a, D, A>,
1531    {
1532        let DocBuilder(allocator, _) = self;
1533        DocBuilder(allocator, before.pretty(allocator).1)
1534            .append(self)
1535            .append(after)
1536    }
1537
1538    pub fn single_quotes(self) -> DocBuilder<'a, D, A> {
1539        self.enclose("'", "'")
1540    }
1541
1542    pub fn double_quotes(self) -> DocBuilder<'a, D, A> {
1543        self.enclose("\"", "\"")
1544    }
1545    pub fn parens(self) -> DocBuilder<'a, D, A> {
1546        self.enclose("(", ")")
1547    }
1548
1549    pub fn angles(self) -> DocBuilder<'a, D, A> {
1550        self.enclose("<", ">")
1551    }
1552    pub fn braces(self) -> DocBuilder<'a, D, A> {
1553        self.enclose("{", "}")
1554    }
1555
1556    pub fn brackets(self) -> DocBuilder<'a, D, A> {
1557        self.enclose("[", "]")
1558    }
1559
1560    pub fn into_doc(self) -> D::Doc {
1561        match self.1 {
1562            BuildDoc::DocPtr(d) => d,
1563            BuildDoc::Doc(d) => self.0.alloc(d),
1564        }
1565    }
1566
1567    fn into_plain_doc(self) -> Doc<'a, D::Doc, A> {
1568        match self.1 {
1569            BuildDoc::DocPtr(_) => unreachable!(),
1570            BuildDoc::Doc(d) => d,
1571        }
1572    }
1573}
1574
1575/// Newtype wrapper for `&Doc`
1576pub struct RefDoc<'a, A = ()>(pub &'a Doc<'a, RefDoc<'a, A>, A>);
1577
1578impl<A> Copy for RefDoc<'_, A> {}
1579impl<A> Clone for RefDoc<'_, A> {
1580    fn clone(&self) -> Self {
1581        *self
1582    }
1583}
1584
1585impl<'a, A> fmt::Debug for RefDoc<'a, A>
1586where
1587    A: fmt::Debug,
1588{
1589    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1590        self.0.fmt(f)
1591    }
1592}
1593
1594impl<'a, A> Deref for RefDoc<'a, A> {
1595    type Target = Doc<'a, RefDoc<'a, A>, A>;
1596
1597    fn deref(&self) -> &Self::Target {
1598        self.0
1599    }
1600}
1601
1602trait DropT {}
1603impl<T> DropT for T {}
1604
1605/// An arena which can be used to allocate `Doc` values.
1606pub struct Arena<'a, A = ()> {
1607    docs: typed_arena::Arena<Doc<'a, RefDoc<'a, A>, A>>,
1608    column_fns: typed_arena::Arena<Box<dyn DropT>>,
1609}
1610
1611impl<A> Default for Arena<'_, A> {
1612    fn default() -> Self {
1613        Self::new()
1614    }
1615}
1616
1617impl<'a, A> Arena<'a, A> {
1618    pub fn new() -> Self {
1619        Arena {
1620            docs: typed_arena::Arena::new(),
1621            column_fns: Default::default(),
1622        }
1623    }
1624
1625    fn alloc_any<T>(&'a self, f: T) -> &'a T
1626    where
1627        T: 'a,
1628    {
1629        let f = Box::new(f);
1630        let f_ptr = &*f as *const T;
1631        // Until #[may_dangle] https://github.com/rust-lang/rust/issues/34761 is stabilized (or
1632        // equivalent) we need to use unsafe to cast away the lifetime of the function as we do not
1633        // have any other way of asserting that the `typed_arena::Arena` destructor does not touch
1634        // `'a`
1635        //
1636        // Since `'a` is used elsewhere in our `Arena` type we still have all the other lifetime
1637        // checks in place (the other arena stores no `Drop` value which touches `'a` which lets it
1638        // compile)
1639        unsafe {
1640            self.column_fns
1641                .alloc(std::mem::transmute::<Box<dyn DropT>, Box<dyn DropT>>(f));
1642            &*f_ptr
1643        }
1644    }
1645}
1646
1647impl<'a, D, A> DocAllocator<'a, A> for &'a D
1648where
1649    D: ?Sized + DocAllocator<'a, A>,
1650    A: 'a,
1651{
1652    type Doc = D::Doc;
1653
1654    #[inline]
1655    fn alloc(&'a self, doc: Doc<'a, Self::Doc, A>) -> Self::Doc {
1656        (**self).alloc(doc)
1657    }
1658
1659    fn alloc_column_fn(
1660        &'a self,
1661        f: impl Fn(usize) -> Self::Doc + 'a,
1662    ) -> <Self::Doc as DocPtr<'a, A>>::ColumnFn {
1663        (**self).alloc_column_fn(f)
1664    }
1665
1666    fn alloc_width_fn(
1667        &'a self,
1668        f: impl Fn(isize) -> Self::Doc + 'a,
1669    ) -> <Self::Doc as DocPtr<'a, A>>::WidthFn {
1670        (**self).alloc_width_fn(f)
1671    }
1672}
1673
1674impl<'a, A> DocAllocator<'a, A> for Arena<'a, A> {
1675    type Doc = RefDoc<'a, A>;
1676
1677    #[inline]
1678    fn alloc(&'a self, doc: Doc<'a, Self::Doc, A>) -> Self::Doc {
1679        RefDoc(match doc {
1680            // Return 'static references for common variants to avoid some allocations
1681            Doc::Nil => &Doc::Nil,
1682            Doc::Hardline => &Doc::Hardline,
1683            Doc::Fail => &Doc::Fail,
1684            // line()
1685            Doc::FlatAlt(RefDoc(Doc::Hardline), RefDoc(Doc::BorrowedText(" "))) => {
1686                &Doc::FlatAlt(RefDoc(&Doc::Hardline), RefDoc(&Doc::BorrowedText(" ")))
1687            }
1688            // line_()
1689            Doc::FlatAlt(RefDoc(Doc::Hardline), RefDoc(Doc::Nil)) => {
1690                &Doc::FlatAlt(RefDoc(&Doc::Hardline), RefDoc(&Doc::Nil))
1691            }
1692            // softline()
1693            Doc::Group(RefDoc(Doc::FlatAlt(
1694                RefDoc(Doc::Hardline),
1695                RefDoc(Doc::BorrowedText(" ")),
1696            ))) => &Doc::Group(RefDoc(&Doc::FlatAlt(
1697                RefDoc(&Doc::Hardline),
1698                RefDoc(&Doc::BorrowedText(" ")),
1699            ))),
1700            // softline_()
1701            Doc::Group(RefDoc(Doc::FlatAlt(RefDoc(Doc::Hardline), RefDoc(Doc::Nil)))) => {
1702                &Doc::Group(RefDoc(&Doc::FlatAlt(
1703                    RefDoc(&Doc::Hardline),
1704                    RefDoc(&Doc::Nil),
1705                )))
1706            }
1707            _ => self.docs.alloc(doc),
1708        })
1709    }
1710
1711    fn alloc_column_fn(
1712        &'a self,
1713        f: impl Fn(usize) -> Self::Doc + 'a,
1714    ) -> <Self::Doc as DocPtr<'a, A>>::ColumnFn {
1715        self.alloc_any(f)
1716    }
1717
1718    fn alloc_width_fn(
1719        &'a self,
1720        f: impl Fn(isize) -> Self::Doc + 'a,
1721    ) -> <Self::Doc as DocPtr<'a, A>>::WidthFn {
1722        self.alloc_any(f)
1723    }
1724}
1725
1726#[cfg(test)]
1727mod tests {
1728    use super::*;
1729
1730    macro_rules! chain {
1731        ($first: expr $(, $rest: expr)* $(,)?) => {{
1732            #[allow(unused_mut)]
1733            let mut doc = DocBuilder(&BoxAllocator, $first.into());
1734            $(
1735                doc = doc.append($rest);
1736            )*
1737            doc.into_doc()
1738        }}
1739    }
1740
1741    #[cfg(target_pointer_width = "64")]
1742    #[test]
1743    fn doc_size() {
1744        // Safeguard against accidentally growing Doc
1745        assert_eq!(8 * 3, std::mem::size_of::<Doc<RefDoc>>());
1746    }
1747
1748    macro_rules! test {
1749        ($size:expr, $actual:expr, $expected:expr) => {
1750            let mut s = String::new();
1751            $actual.render_fmt($size, &mut s).unwrap();
1752            difference::assert_diff!(&s, $expected, "\n", 0);
1753        };
1754        ($actual:expr, $expected:expr) => {
1755            test!(70, $actual, $expected)
1756        };
1757    }
1758
1759    #[test]
1760    fn box_doc_inference() {
1761        let doc: BoxDoc<()> = BoxDoc::group(
1762            BoxDoc::text("test")
1763                .append(BoxDoc::line())
1764                .append(BoxDoc::text("test")),
1765        );
1766
1767        test!(doc, "test test");
1768    }
1769
1770    #[test]
1771    fn newline_in_text() {
1772        let doc: BoxDoc<()> = BoxDoc::group(
1773            BoxDoc::text("test").append(
1774                BoxDoc::line()
1775                    .append(BoxDoc::text("\"test\n     test\""))
1776                    .nest(4),
1777            ),
1778        );
1779
1780        test!(5, doc, "test\n    \"test\n     test\"");
1781    }
1782
1783    #[test]
1784    fn forced_newline() {
1785        let doc: BoxDoc<()> = BoxDoc::group(
1786            BoxDoc::text("test")
1787                .append(BoxDoc::hardline())
1788                .append(BoxDoc::text("test")),
1789        );
1790
1791        test!(doc, "test\ntest");
1792    }
1793
1794    #[test]
1795    fn space_do_not_reset_pos() {
1796        let doc: BoxDoc<()> = BoxDoc::group(BoxDoc::text("test").append(BoxDoc::line()))
1797            .append(BoxDoc::text("test"))
1798            .append(BoxDoc::group(BoxDoc::line()).append(BoxDoc::text("test")));
1799
1800        test!(9, doc, "test test\ntest");
1801    }
1802
1803    // Tests that the `BoxDoc::hardline()` does not cause the rest of document to think that it fits on
1804    // a single line but instead breaks on the `BoxDoc::line()` to fit with 6 columns
1805    #[test]
1806    fn newline_does_not_cause_next_line_to_be_to_long() {
1807        let doc: RcDoc<()> = RcDoc::group(
1808            RcDoc::text("test").append(RcDoc::hardline()).append(
1809                RcDoc::text("test")
1810                    .append(RcDoc::line())
1811                    .append(RcDoc::text("test")),
1812            ),
1813        );
1814
1815        test!(6, doc, "test\ntest\ntest");
1816    }
1817
1818    #[test]
1819    fn newline_after_group_does_not_affect_it() {
1820        let arena = Arena::<()>::new();
1821        let doc = arena.text("x").append(arena.line()).append("y").group();
1822
1823        test!(100, doc.append(arena.hardline()).1, "x y\n");
1824    }
1825
1826    #[test]
1827    fn block() {
1828        let doc: RcDoc<()> = RcDoc::group(
1829            RcDoc::text("{")
1830                .append(
1831                    RcDoc::line()
1832                        .append(RcDoc::text("test"))
1833                        .append(RcDoc::line())
1834                        .append(RcDoc::text("test"))
1835                        .nest(2),
1836                )
1837                .append(RcDoc::line())
1838                .append(RcDoc::text("}")),
1839        );
1840
1841        test!(5, doc, "{\n  test\n  test\n}");
1842    }
1843
1844    #[test]
1845    fn block_with_hardline() {
1846        let doc: RcDoc<()> = RcDoc::group(
1847            RcDoc::text("{")
1848                .append(
1849                    RcDoc::line()
1850                        .append(RcDoc::text("test"))
1851                        .append(RcDoc::hardline())
1852                        .append(RcDoc::text("test"))
1853                        .nest(2),
1854                )
1855                .append(RcDoc::line())
1856                .append(RcDoc::text("}")),
1857        );
1858
1859        test!(10, doc, "{\n  test\n  test\n}");
1860    }
1861
1862    #[test]
1863    fn block_with_hardline_negative_nest() {
1864        let doc: RcDoc<()> = RcDoc::group(
1865            RcDoc::text("{")
1866                .append(
1867                    RcDoc::line()
1868                        .append(RcDoc::text("test"))
1869                        .append(RcDoc::hardline())
1870                        .append(RcDoc::text("test"))
1871                        .nest(-2),
1872                )
1873                .append(RcDoc::line())
1874                .append(RcDoc::text("}")),
1875        );
1876
1877        test!(10, doc, "{\ntest\ntest\n}");
1878    }
1879
1880    #[test]
1881    fn line_comment() {
1882        let doc: BoxDoc<()> = BoxDoc::group(
1883            BoxDoc::text("{")
1884                .append(
1885                    BoxDoc::line()
1886                        .append(BoxDoc::text("test"))
1887                        .append(BoxDoc::line())
1888                        .append(BoxDoc::text("// a").append(BoxDoc::hardline()))
1889                        .append(BoxDoc::text("test"))
1890                        .nest(2),
1891                )
1892                .append(BoxDoc::line())
1893                .append(BoxDoc::text("}")),
1894        );
1895
1896        test!(14, doc, "{\n  test\n  // a\n  test\n}");
1897    }
1898
1899    #[test]
1900    fn annotation_no_panic() {
1901        let doc: BoxDoc<()> = BoxDoc::group(
1902            BoxDoc::text("test")
1903                .annotate(())
1904                .append(BoxDoc::hardline())
1905                .annotate(())
1906                .append(BoxDoc::text("test")),
1907        );
1908
1909        test!(doc, "test\ntest");
1910    }
1911
1912    fn nest_on_line(doc: BoxDoc<'static, ()>) -> BoxDoc<'static, ()> {
1913        BoxDoc::softline().append(BoxDoc::nesting(move |n| {
1914            let doc = doc.clone();
1915            BoxDoc::column(move |c| {
1916                if n == c {
1917                    BoxDoc::text("  ").append(doc.clone()).nest(2)
1918                } else {
1919                    doc.clone()
1920                }
1921            })
1922        }))
1923    }
1924
1925    #[test]
1926    fn hang_lambda1() {
1927        let doc = chain![
1928            chain!["let", BoxDoc::line(), "x", BoxDoc::line(), "="].group(),
1929            nest_on_line(chain![
1930                "\\y ->",
1931                chain![BoxDoc::line(), "y"].nest(2).group()
1932            ]),
1933        ]
1934        .group();
1935
1936        test!(doc, "let x = \\y -> y");
1937        test!(
1938            8,
1939            doc,
1940            r"let x =
1941  \y ->
1942    y"
1943        );
1944        test!(
1945            14,
1946            doc,
1947            r"let x = \y ->
1948  y"
1949        );
1950    }
1951
1952    #[test]
1953    fn hang_comment() {
1954        let body = chain!["y"].nest(2).group();
1955        let doc = chain![
1956            chain!["let", BoxDoc::line(), "x", BoxDoc::line(), "="].group(),
1957            nest_on_line(chain![
1958                "\\y ->",
1959                nest_on_line(chain!["// abc", BoxDoc::hardline(), body])
1960            ]),
1961        ]
1962        .group();
1963
1964        test!(8, doc, "let x =\n  \\y ->\n    // abc\n    y");
1965        test!(14, doc, "let x = \\y ->\n  // abc\n  y");
1966    }
1967
1968    #[test]
1969    fn union() {
1970        let doc = chain![
1971            chain!["let", BoxDoc::line(), "x", BoxDoc::line(), "="].group(),
1972            nest_on_line(chain![
1973                "(",
1974                chain![
1975                    BoxDoc::line_(),
1976                    chain!["x", ","].group(),
1977                    BoxDoc::line(),
1978                    chain!["1234567890", ","].group()
1979                ]
1980                .nest(2)
1981                .group(),
1982                BoxDoc::line_().append(")"),
1983            ])
1984        ]
1985        .group();
1986
1987        test!(doc, "let x = (x, 1234567890,)");
1988        test!(8, doc, "let x =\n  (\n    x,\n    1234567890,\n  )");
1989        test!(14, doc, "let x = (\n  x,\n  1234567890,\n)");
1990    }
1991
1992    fn hang2(
1993        from: BoxDoc<'static, ()>,
1994        body_whitespace: BoxDoc<'static, ()>,
1995        body: BoxDoc<'static, ()>,
1996        trailer: BoxDoc<'static, ()>,
1997    ) -> BoxDoc<'static, ()> {
1998        let body1 = body_whitespace
1999            .append(body.clone())
2000            .nest(2)
2001            .group()
2002            .append(trailer.clone());
2003        let body2 = BoxDoc::hardline()
2004            .append(body.clone())
2005            .nest(2)
2006            .group()
2007            .append(trailer.clone());
2008
2009        let single = from.clone().append(body1.clone()).group();
2010
2011        let hang = from.clone().append(body2).group();
2012
2013        let break_all = from.append(body1).group().nest(2);
2014
2015        BoxDoc::group(single.union(hang.union(break_all)))
2016    }
2017
2018    #[test]
2019    fn hang_lambda2() {
2020        let from = chain![
2021            chain!["let", BoxDoc::line(), "x", BoxDoc::line(), "="].group(),
2022            BoxDoc::line(),
2023            "\\y ->",
2024        ]
2025        .group();
2026
2027        let body = chain!["y"].group();
2028
2029        let trailer = BoxDoc::nil();
2030
2031        let doc = hang2(from, BoxDoc::line(), body, trailer);
2032        eprintln!("{:#?}", doc);
2033
2034        test!(doc, "let x = \\y -> y");
2035        test!(14, doc, "let x = \\y ->\n  y");
2036    }
2037
2038    #[test]
2039    fn union2() {
2040        let from = chain![
2041            chain!["let", BoxDoc::line(), "x", BoxDoc::line(), "="].group(),
2042            BoxDoc::line(),
2043            "(",
2044        ]
2045        .group();
2046
2047        let body = chain![
2048            chain!["x", ","].group(),
2049            BoxDoc::line(),
2050            chain!["1234567890", ","].group()
2051        ]
2052        .group();
2053
2054        let trailer = BoxDoc::line_().append(")");
2055
2056        let doc = hang2(from, BoxDoc::line_(), body, trailer);
2057
2058        test!(doc, "let x = (x, 1234567890,)");
2059        test!(14, doc, "let x = (\n  x,\n  1234567890,\n)");
2060    }
2061
2062    #[test]
2063    fn usize_max_value() {
2064        let doc: BoxDoc<()> = BoxDoc::group(
2065            BoxDoc::text("test")
2066                .append(BoxDoc::line())
2067                .append(BoxDoc::text("test")),
2068        );
2069
2070        test!(usize::max_value(), doc, "test test");
2071    }
2072
2073    #[test]
2074    fn fail() {
2075        let fail_break: BoxDoc<()> = BoxDoc::fail().flat_alt(Doc::nil());
2076
2077        let doc = fail_break.append(Doc::text("12345")).group().union("abc");
2078
2079        test!(5, doc, "12345");
2080        test!(4, doc, "abc");
2081    }
2082
2083    pub struct TestWriter<W> {
2084        upstream: W,
2085    }
2086
2087    impl<W> TestWriter<W> {
2088        pub fn new(upstream: W) -> Self {
2089            Self { upstream }
2090        }
2091    }
2092
2093    impl<W> Render for TestWriter<W>
2094    where
2095        W: Render,
2096    {
2097        type Error = W::Error;
2098
2099        fn write_str(&mut self, s: &str) -> Result<usize, W::Error> {
2100            self.upstream.write_str(s)
2101        }
2102
2103        fn write_str_all(&mut self, s: &str) -> Result<(), W::Error> {
2104            self.upstream.write_str_all(s)
2105        }
2106
2107        fn fail_doc(&self) -> Self::Error {
2108            self.upstream.fail_doc()
2109        }
2110    }
2111
2112    impl<W> RenderAnnotated<'_, ()> for TestWriter<W>
2113    where
2114        W: Render,
2115    {
2116        fn push_annotation(&mut self, _: &()) -> Result<(), Self::Error> {
2117            self.upstream.write_str_all("[")
2118        }
2119
2120        fn pop_annotation(&mut self) -> Result<(), Self::Error> {
2121            self.upstream.write_str_all("]")
2122        }
2123    }
2124
2125    #[test]
2126    fn annotations() {
2127        let actual = BoxDoc::text("abc").annotate(()).annotate(());
2128        let mut s = String::new();
2129        actual
2130            .render_raw(70, &mut TestWriter::new(FmtWrite::new(&mut s)))
2131            .unwrap();
2132        difference::assert_diff!(&s, "[[abc]]", "\n", 0);
2133    }
2134
2135    #[test]
2136    fn non_ascii_is_not_byte_length() {
2137        let doc: BoxDoc<()> = BoxDoc::group(
2138            BoxDoc::text("ÅÄÖ")
2139                .append(BoxDoc::line())
2140                .append(BoxDoc::text("test")),
2141        );
2142
2143        test!(8, doc, "ÅÄÖ test");
2144    }
2145
2146    #[test]
2147    fn cjk_display_width() {
2148        let arena = Arena::<()>::new();
2149        let doc = arena
2150            .text("你好")
2151            .append(arena.line().append(arena.text("abc")).align())
2152            .into_doc();
2153
2154        test!(doc, "你好\n    abc");
2155    }
2156
2157    #[test]
2158    fn pretty_cow() {
2159        let doc: BoxDoc<()> = docs![
2160            &BoxAllocator,
2161            Cow::<str>::Borrowed("abc"),
2162            BoxDoc::line(),
2163            Cow::<str>::Owned("123".to_string()),
2164        ]
2165        .group()
2166        .into_doc();
2167
2168        test!(8, doc, "abc 123");
2169    }
2170
2171    #[test]
2172    fn stress_append_left_assoc() {
2173        let arena = Arena::new();
2174        let mut doc: DocBuilder<'_, Arena<'_, _>, char> = arena.nil();
2175        for _ in 0..100000 {
2176            doc = doc.append("a");
2177        }
2178        let mut s = String::new();
2179        doc.render_fmt(80, &mut s).unwrap();
2180    }
2181
2182    #[test]
2183    fn stress_append_right_assoc() {
2184        let arena = Arena::new();
2185        let mut doc: RefDoc<'_, char> = arena.nil().into_doc();
2186        for _ in 0..100000 {
2187            doc = arena.text("a").append(doc).into_doc();
2188        }
2189        let mut s = String::new();
2190        doc.render_fmt(80, &mut s).unwrap();
2191    }
2192}