(&self, width: usize, out: &mut W) -> Result<(), W::Error>
where
for<'b> W: render::RenderAnnotated<'b, A>,
W: ?Sized,
{
render::best(self, width, out)
}
/// Returns a value which implements `std::fmt::Display`
///
/// ```
/// use pretty::{Doc, BoxDoc};
/// let doc = BoxDoc::<()>::group(
/// BoxDoc::text("hello").append(Doc::line()).append(Doc::text("world"))
/// );
/// assert_eq!(format!("{}", doc.pretty(80)), "hello world");
/// ```
#[inline]
pub fn pretty<'d>(&'d self, width: usize) -> PrettyFmt<'a, 'd, T, A> {
PrettyFmt { doc: self, width }
}
}
#[cfg(feature = "termcolor")]
impl<'a, T> Doc<'a, T, ColorSpec>
where
T: DocPtr<'a, ColorSpec> + 'a,
{
#[inline]
pub fn render_colored(&self, width: usize, out: W) -> io::Result<()>
where
W: WriteColor,
{
render::best(self, width, &mut TermColored::new(out))
}
}
/// The `DocBuilder` type allows for convenient appending of documents even for arena allocated
/// documents by storing the arena inline.
pub struct DocBuilder<'a, D, A = ()>(pub &'a D, pub BuildDoc<'a, D::Doc, A>)
where
D: ?Sized + DocAllocator<'a, A>;
impl<'a, D, A, P> Add for DocBuilder<'a, D, A>
where
D: ?Sized + DocAllocator<'a, A>,
P: Pretty<'a, D, A>,
{
type Output = DocBuilder<'a, D, A>;
fn add(self, other: P) -> Self::Output {
self.append(other)
}
}
impl<'a, D, A, P> AddAssign
for DocBuilder<'a, D, A>
where
D: ?Sized + DocAllocator<'a, A>,
P: Pretty<'a, D, A>,
{
fn add_assign(&mut self, other: P) {
*self = DocBuilder(self.0, std::mem::take(&mut self.1)).append(other)
}
}
impl<'a, D, A> Deref for DocBuilder<'a, D, A>
where
D: ?Sized + DocAllocator<'a, A>,
{
type Target = Doc<'a, D::Doc, A>;
fn deref(&self) -> &Self::Target {
match &self.1 {
BuildDoc::DocPtr(d) => d,
BuildDoc::Doc(d) => d,
}
}
}
impl<'a, D, A> fmt::Debug for DocBuilder<'a, D, A>
where
D: ?Sized + DocAllocator<'a, A>,
D::Doc: fmt::Debug,
A: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.1.fmt(f)
}
}
impl<'a, A, D> Clone for DocBuilder<'a, D, A>
where
A: Clone,
D: DocAllocator<'a, A> + 'a,
D::Doc: Clone,
{
fn clone(&self) -> Self {
DocBuilder(self.0, self.1.clone())
}
}
impl<'a, D, A> From> for BuildDoc<'a, D::Doc, A>
where
D: ?Sized + DocAllocator<'a, A>,
{
fn from(val: DocBuilder<'a, D, A>) -> Self {
val.1
}
}
pub trait DocPtr<'a, A>: Deref> + Sized
where
A: 'a,
{
type ColumnFn: Deref Self + 'a> + Clone + 'a;
type WidthFn: Deref Self + 'a> + Clone + 'a;
}
impl<'a, A> DocPtr<'a, A> for RefDoc<'a, A> {
type ColumnFn = &'a (dyn Fn(usize) -> Self + 'a);
type WidthFn = &'a (dyn Fn(isize) -> Self + 'a);
}
/// Trait for types which can be converted to a `Document`
pub trait Pretty<'a, D, A = ()>
where
A: 'a,
D: ?Sized + DocAllocator<'a, A>,
{
/// Converts `self` into a document
fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A>;
}
impl<'a, A> Pretty<'a, BoxAllocator, A> for BoxDoc<'a, A>
where
A: 'a,
{
fn pretty(self, allocator: &'a BoxAllocator) -> DocBuilder<'a, BoxAllocator, A> {
DocBuilder(allocator, self.into())
}
}
impl<'a, A> Pretty<'a, RcAllocator, A> for RcDoc<'a, A>
where
A: 'a,
{
fn pretty(self, allocator: &'a RcAllocator) -> DocBuilder<'a, RcAllocator, A> {
DocBuilder(allocator, self.into())
}
}
impl<'a, A> Pretty<'a, Arena<'a, A>, A> for RefDoc<'a, A>
where
A: 'a,
{
fn pretty(self, allocator: &'a Arena<'a, A>) -> DocBuilder<'a, Arena<'a, A>, A> {
DocBuilder(allocator, self.into())
}
}
impl<'a, D, A> Pretty<'a, D, A> for BuildDoc<'a, D::Doc, A>
where
A: 'a,
D: ?Sized + DocAllocator<'a, A>,
{
fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> {
DocBuilder(allocator, self)
}
}
impl<'a, D, A> Pretty<'a, D, A> for Doc<'a, D::Doc, A>
where
A: 'a,
D: ?Sized + DocAllocator<'a, A>,
{
fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> {
DocBuilder(allocator, self.into())
}
}
impl<'a, D, A> Pretty<'a, D, A> for DocBuilder<'a, D, A>
where
A: 'a,
D: ?Sized + DocAllocator<'a, A>,
{
fn pretty(self, _: &'a D) -> DocBuilder<'a, D, A> {
self
}
}
impl<'a, D, A, T> Pretty<'a, D, A> for Option
where
A: 'a,
D: ?Sized + DocAllocator<'a, A>,
T: Pretty<'a, D, A>,
{
fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> {
match self {
Some(x) => x.pretty(allocator),
None => allocator.nil(),
}
}
}
impl<'a, D, A> Pretty<'a, D, A> for &'a str
where
A: 'a,
D: ?Sized + DocAllocator<'a, A>,
{
fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> {
allocator.text(self)
}
}
impl<'a, D, A> Pretty<'a, D, A> for &'a String
where
A: 'a,
D: ?Sized + DocAllocator<'a, A>,
{
fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> {
self[..].pretty(allocator)
}
}
impl<'a, D, A> Pretty<'a, D, A> for String
where
A: 'a,
D: ?Sized + DocAllocator<'a, A>,
{
fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> {
allocator.text(self)
}
}
impl<'a, D, A, S> Pretty<'a, D, A> for Cow<'a, S>
where
A: 'a,
D: ?Sized + DocAllocator<'a, A>,
S: ?Sized + ToOwned,
&'a S: Pretty<'a, D, A>,
S::Owned: Pretty<'a, D, A>,
{
fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> {
match self {
Cow::Borrowed(s) => s.pretty(allocator),
Cow::Owned(s) => s.pretty(allocator),
}
}
}
/// The `DocAllocator` trait abstracts over a type which can allocate (pointers to) `Doc`.
pub trait DocAllocator<'a, A = ()>
where
A: 'a,
{
type Doc: DocPtr<'a, A>;
fn alloc(&'a self, doc: Doc<'a, Self::Doc, A>) -> Self::Doc;
fn alloc_column_fn(
&'a self,
f: impl Fn(usize) -> Self::Doc + 'a,
) -> >::ColumnFn;
fn alloc_width_fn(
&'a self,
f: impl Fn(isize) -> Self::Doc + 'a,
) -> >::WidthFn;
fn alloc_cow(&'a self, doc: BuildDoc<'a, Self::Doc, A>) -> Self::Doc {
match doc {
BuildDoc::DocPtr(d) => d,
BuildDoc::Doc(d) => self.alloc(d),
}
}
/// Allocate an empty document.
#[inline]
fn nil(&'a self) -> DocBuilder<'a, Self, A> {
DocBuilder(self, Doc::Nil.into())
}
/// Fails document rendering immediately.
///
/// Primarily used to abort rendering inside the left side of `Union`
#[inline]
fn fail(&'a self) -> DocBuilder<'a, Self, A> {
DocBuilder(self, Doc::Fail.into())
}
/// Allocate a single hardline.
#[inline]
fn hardline(&'a self) -> DocBuilder<'a, Self, A> {
DocBuilder(self, Doc::Hardline.into())
}
#[inline]
fn space(&'a self) -> DocBuilder<'a, Self, A> {
self.text(" ")
}
/// A line acts like a `\n` but behaves like `space` if it is grouped on a single line.
#[inline]
fn line(&'a self) -> DocBuilder<'a, Self, A> {
self.hardline().flat_alt(self.space())
}
/// Acts like `line` but behaves like `nil` if grouped on a single line
///
/// ```
/// use pretty::{Doc, RcDoc};
///
/// let doc = RcDoc::<()>::group(
/// RcDoc::text("(")
/// .append(
/// RcDoc::line_()
/// .append(Doc::text("test"))
/// .append(Doc::line())
/// .append(Doc::text("test"))
/// .nest(2),
/// )
/// .append(Doc::line_())
/// .append(Doc::text(")")),
/// );
/// assert_eq!(doc.pretty(5).to_string(), "(\n test\n test\n)");
/// assert_eq!(doc.pretty(100).to_string(), "(test test)");
/// ```
#[inline]
fn line_(&'a self) -> DocBuilder<'a, Self, A> {
self.hardline().flat_alt(self.nil())
}
/// A `softline` acts like `space` if the document fits the page, otherwise like `line`
#[inline]
fn softline(&'a self) -> DocBuilder<'a, Self, A> {
self.line().group()
}
/// A `softline_` acts like `nil` if the document fits the page, otherwise like `line_`
#[inline]
fn softline_(&'a self) -> DocBuilder<'a, Self, A> {
self.line_().group()
}
/// Allocate a document containing the text `t.to_string()`.
///
/// The given text must not contain line breaks.
#[inline]
fn as_string(&'a self, data: U) -> DocBuilder<'a, Self, A> {
use std::fmt::Write;
let mut buf = FmtText::Small(SmallText::new());
write!(buf, "{}", data).unwrap();
let doc = match buf {
FmtText::Small(b) => Doc::SmallText(b),
FmtText::Large(b) => Doc::OwnedText(b.into()),
};
DocBuilder(self, doc.into()).with_utf8_len()
}
/// Allocate a document containing the given text.
///
/// The given text must not contain line breaks.
#[inline]
fn text>>(&'a self, data: U) -> DocBuilder<'a, Self, A> {
let data: Cow<_> = data.into();
let doc = if data.is_empty() {
Doc::Nil.into()
} else {
match data {
Cow::Owned(t) => Doc::OwnedText(t.into()).into(),
Cow::Borrowed(t) => Doc::BorrowedText(t).into(),
}
};
DocBuilder(self, doc).with_utf8_len()
}
/// Allocate a document concatenating the given documents.
#[inline]
fn concat(&'a self, docs: I) -> DocBuilder<'a, Self, A>
where
I: IntoIterator,
I::Item: Pretty<'a, Self, A>,
{
docs.into_iter().fold(self.nil(), |a, b| a.append(b))
}
/// Allocate a document that intersperses the given separator `S` between the given documents
/// `[A, B, C, ..., Z]`, yielding `[A, S, B, S, C, S, ..., S, Z]`.
///
/// Compare [the `intersperse` method from the `itertools` crate](https://docs.rs/itertools/0.5.9/itertools/trait.Itertools.html#method.intersperse).
///
/// NOTE: The separator type, `S` may need to be cloned. Consider using cheaply cloneable ptr
/// like `RefDoc` or `RcDoc`
#[inline]
fn intersperse(&'a self, docs: I, separator: S) -> DocBuilder<'a, Self, A>
where
I: IntoIterator,
I::Item: Pretty<'a, Self, A>,
S: Pretty<'a, Self, A> + Clone,
{
let mut result = self.nil();
let mut iter = docs.into_iter();
if let Some(first) = iter.next() {
result = result.append(first);
for doc in iter {
result = result.append(separator.clone());
result = result.append(doc);
}
}
result
}
/// Allocate a document that acts differently based on the position and page layout
///
/// ```rust
/// use pretty::DocAllocator;
///
/// let arena = pretty::Arena::<()>::new();
/// let doc = arena.text("prefix ")
/// .append(arena.column(|l| {
/// arena.text("| <- column ").append(arena.as_string(l)).into_doc()
/// }));
/// assert_eq!(doc.1.pretty(80).to_string(), "prefix | <- column 7");
/// ```
#[inline]
fn column(&'a self, f: impl Fn(usize) -> Self::Doc + 'a) -> DocBuilder<'a, Self, A> {
DocBuilder(self, Doc::Column(self.alloc_column_fn(f)).into())
}
/// Allocate a document that acts differently based on the current nesting level
///
/// ```rust
/// use pretty::DocAllocator;
///
/// let arena = pretty::Arena::<()>::new();
/// let doc = arena.text("prefix ")
/// .append(arena.nesting(|l| {
/// arena.text("[Nested: ").append(arena.as_string(l)).append("]").into_doc()
/// }).nest(4));
/// assert_eq!(doc.1.pretty(80).to_string(), "prefix [Nested: 4]");
/// ```
#[inline]
fn nesting(&'a self, f: impl Fn(usize) -> Self::Doc + 'a) -> DocBuilder<'a, Self, A> {
DocBuilder(self, Doc::Nesting(self.alloc_column_fn(f)).into())
}
/// Reflows `text` inserting `softline` in place of any whitespace
#[inline]
fn reflow(&'a self, text: &'a str) -> DocBuilder<'a, Self, A>
where
Self: Sized,
Self::Doc: Clone,
A: Clone,
{
self.intersperse(text.split(char::is_whitespace), self.softline())
}
}
/// Either a `Doc` or a pointer to a `Doc` (`D`)
#[derive(Clone)]
pub enum BuildDoc<'a, D, A>
where
D: DocPtr<'a, A>,
{
DocPtr(D),
Doc(Doc<'a, D, A>),
}
impl<'a, D, A> Default for BuildDoc<'a, D, A>
where
D: DocPtr<'a, A>,
{
fn default() -> Self {
Self::Doc(Doc::default())
}
}
impl<'a, D, A> fmt::Debug for BuildDoc<'a, D, A>
where
D: DocPtr<'a, A> + fmt::Debug,
A: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(**self).fmt(f)
}
}
impl<'a, D, A> Deref for BuildDoc<'a, D, A>
where
D: DocPtr<'a, A>,
{
type Target = Doc<'a, D, A>;
fn deref(&self) -> &Self::Target {
match self {
BuildDoc::DocPtr(d) => d,
BuildDoc::Doc(d) => d,
}
}
}
impl<'a, A> From> for BuildDoc<'a, RefDoc<'a, A>, A> {
fn from(s: RefDoc<'a, A>) -> Self {
BuildDoc::DocPtr(s)
}
}
impl<'a, A> From> for BuildDoc<'a, BoxDoc<'a, A>, A> {
fn from(s: BoxDoc<'a, A>) -> Self {
BuildDoc::DocPtr(s)
}
}
impl<'a, A> From> for BuildDoc<'a, RcDoc<'a, A>, A> {
fn from(s: RcDoc<'a, A>) -> Self {
BuildDoc::DocPtr(s)
}
}
impl<'a, T, A> From> for BuildDoc<'a, T, A>
where
T: DocPtr<'a, A>,
{
fn from(s: Doc<'a, T, A>) -> Self {
BuildDoc::Doc(s)
}
}
impl<'a, T, A> From for BuildDoc<'a, T, A>
where
T: StaticDoc<'a, A>,
{
fn from(s: String) -> Self {
BuildDoc::Doc(Doc::text(s))
}
}
impl<'a, T, A> From<&'a str> for BuildDoc<'a, T, A>
where
T: StaticDoc<'a, A>,
{
fn from(s: &'a str) -> Self {
BuildDoc::Doc(Doc::text(s))
}
}
impl<'a, T, A> From<&'a String> for BuildDoc<'a, T, A>
where
T: StaticDoc<'a, A>,
{
fn from(s: &'a String) -> Self {
BuildDoc::Doc(Doc::text(s))
}
}
impl<'a, T, A, S> From