Skip to main content

Doc

Struct Doc 

Source
pub struct Doc(/* private fields */);
Expand description

An immutable, cheaply-clonable description of a document’s layout.

A Doc records intent — “these pieces belong together”, “break here if the line is too long”, “indent the inside by four” — and leaves the choice of concrete line breaks to render, which fits the document to a target width. The same Doc renders differently at width 40 and width 120 with no change to how it was built.

§Cloning

Doc is a thin handle around a reference-counted node (Rc), so Clone is a pointer-count bump, not a deep copy. Sharing a sub-document in several places costs one Rc clone each. Doc is single-threaded by design (it is not Send/Sync); a formatter builds and renders on one thread.

§Examples

use pretty_lang::Doc;

// `[1, 2, 3]` — flat because it fits.
let list = Doc::text("[")
    .append(Doc::join(
        Doc::text(",").append(Doc::line()),
        ["1", "2", "3"].map(Doc::text),
    ))
    .append(Doc::text("]"))
    .group();

assert_eq!(list.render(80), "[1, 2, 3]");

Implementations§

Source§

impl Doc

Source

pub fn nil() -> Doc

The empty document. It renders to nothing and is the identity for append.

§Examples
use pretty_lang::Doc;

assert_eq!(Doc::nil().render(80), "");
assert_eq!(Doc::text("x").append(Doc::nil()).render(80), "x");
Source

pub fn text(s: impl Into<Cow<'static, str>>) -> Doc

A literal piece of text.

The argument is anything that converts into a Cow<'static, str>, so a string literal is stored without allocating and an owned String is moved in. The display width is measured once, here, as the number of Unicode scalar values.

§Panics

Never panics. The text is treated as a single unbreakable unit; it MUST NOT contain a '\n' (embed line breaks with line, softline, or hardline so the layout engine can account for them). A newline inside text is rendered verbatim but throws the width accounting off.

§Examples
use pretty_lang::Doc;

// A static literal — no allocation.
assert_eq!(Doc::text("let x").render(80), "let x");

// An owned, computed string.
let name = format!("v{}", 42);
assert_eq!(Doc::text(name).render(80), "v42");
Source

pub fn line() -> Doc

A flexible break that is a single space when its group is laid out flat and a newline (plus the current indentation) when the group breaks.

This is the workhorse separator: put it between items that should sit on one line when they fit and stack one-per-line when they do not.

§Examples
use pretty_lang::Doc;

let doc = Doc::text("a").append(Doc::line()).append(Doc::text("b")).group();
assert_eq!(doc.render(80), "a b");   // fits: space
assert_eq!(doc.render(1), "a\nb");   // too narrow: newline
Source

pub fn softline() -> Doc

A flexible break that is nothing when its group is flat and a newline (plus indentation) when the group breaks. Use it where a broken layout wants a line break but a flat layout wants no space at all — for example right after an opening bracket.

§Examples
use pretty_lang::Doc;

let doc = Doc::text("(")
    .append(Doc::softline())
    .append(Doc::text("x"))
    .group();
assert_eq!(doc.render(80), "(x");  // flat: no gap
Source

pub fn hardline() -> Doc

A break that is always a newline, and forces every group that contains it to break. Use it for constructs that must never be collapsed onto one line, such as line comments or statement separators in block bodies.

§Examples
use pretty_lang::Doc;

let doc = Doc::text("a").append(Doc::hardline()).append(Doc::text("b")).group();
// Even though "a b" would fit at width 80, the hardline forces a break.
assert_eq!(doc.render(80), "a\nb");
Source

pub fn append(self, other: Doc) -> Doc

Concatenate self with other, laid out left then right. This is the fundamental way to build a document up from parts.

nil is the identity: a.append(Doc::nil()) and Doc::nil().append(a) both render exactly as a.

§Examples
use pretty_lang::Doc;

let doc = Doc::text("fn ").append(Doc::text("main")).append(Doc::text("()"));
assert_eq!(doc.render(80), "fn main()");
Source

pub fn nest(self, indent: isize) -> Doc

Increase the indentation applied to every line break inside self by indent columns. Indentation is relative and nests: an inner nest(4) inside an outer nest(4) indents broken lines by eight.

indent is an isize; a negative value dedents. The effective indentation never goes below zero (it is clamped at the point a newline is emitted).

Only line breaks that actually happen are affected — nest on a document that stays flat has no visible effect.

§Examples
use pretty_lang::Doc;

let body = Doc::text("{")
    .append(
        Doc::line()
            .append(Doc::text("stmt;"))
            .nest(4),
    )
    .append(Doc::line())
    .append(Doc::text("}"))
    .group();

assert_eq!(body.render(4), "{\n    stmt;\n}");
Source

pub fn group(self) -> Doc

Mark self as a layout choice point.

When the renderer reaches a group it first asks whether the group’s contents fit, laid out flat, in the width remaining on the current line. If they do, every flexible break inside becomes its flat form (a space or nothing). If they do not — or the group contains a hardline — every flexible break inside becomes a newline. The decision is all-or-nothing for the breaks directly owned by this group; nested groups are decided independently.

Grouping is what turns one document into “one line if it fits, otherwise stacked”. A document with no groups always uses the broken form of every break.

§Examples
use pretty_lang::Doc;

let call = Doc::text("f(")
    .append(
        Doc::softline()
            .append(Doc::join(
                Doc::text(",").append(Doc::line()),
                ["alpha", "beta", "gamma"].map(Doc::text),
            ))
            .nest(4),
    )
    .append(Doc::softline())
    .append(Doc::text(")"))
    .group();

assert_eq!(call.render(80), "f(alpha, beta, gamma)");
assert_eq!(
    call.render(10),
    "f(\n    alpha,\n    beta,\n    gamma\n)"
);
Source

pub fn concat(docs: impl IntoIterator<Item = Doc>) -> Doc

Concatenate every document produced by docs, in order. Returns nil for an empty iterator.

This is a left fold of append and allocates one internal node per item.

§Examples
use pretty_lang::Doc;

let doc = Doc::concat(["a", "b", "c"].map(Doc::text));
assert_eq!(doc.render(80), "abc");

assert_eq!(Doc::concat(core::iter::empty()).render(80), "");
Source

pub fn join(sep: Doc, docs: impl IntoIterator<Item = Doc>) -> Doc

Concatenate every document produced by docs, placing a clone of sep between consecutive items (but not before the first or after the last). Returns nil for an empty iterator.

This is the idiomatic way to render comma-separated lists, &&-joined conditions, ::-joined paths, and the like — pair it with group so the whole list collapses onto one line when it fits.

§Examples
use pretty_lang::Doc;

let path = Doc::join(Doc::text("::"), ["std", "collections", "HashMap"].map(Doc::text));
assert_eq!(path.render(80), "std::collections::HashMap");

// With a flexible separator, the list reflows under a group.
let args = Doc::join(
    Doc::text(",").append(Doc::line()),
    ["x", "y"].map(Doc::text),
)
.group();
assert_eq!(args.render(80), "x, y");
Source

pub fn render(&self, width: usize) -> String

Render this document to an owned String, choosing line breaks so that no line exceeds width columns where the document allows a choice.

width is the target line length in Unicode scalars. Lines can still exceed it when a single unbreakable text is wider than width, or where the document offers no break — the renderer never invents break points that were not described.

§Examples
use pretty_lang::Doc;

let doc = Doc::text("a").append(Doc::line()).append(Doc::text("b")).group();
assert_eq!(doc.render(80), "a b");
assert_eq!(doc.render(1), "a\nb");
Source

pub fn render_into<W: Write>(&self, width: usize, out: &mut W) -> Result

Render this document into any core::fmt::Write sink, choosing line breaks for the target width. Use this to stream directly into a caller -owned buffer and avoid the intermediate String that render allocates.

§Errors

Returns core::fmt::Error if and only if the underlying out returns an error while being written to.

§Examples
use core::fmt::Write;
use pretty_lang::Doc;

let doc = Doc::text("hello").append(Doc::text(" world"));
let mut buf = String::new();
doc.render_into(80, &mut buf).unwrap();
assert_eq!(buf, "hello world");
Source

pub fn render_writer<W: Write>(&self, width: usize, out: &mut W) -> Result<()>

Available on crate feature std only.

Render this document into a std::io::Write sink, choosing line breaks for the target width. This is the streaming counterpart to render for files, sockets, and stdout.

§Errors

Propagates the first std::io::Error returned by out.

§Examples
use pretty_lang::Doc;

let doc = Doc::text("written to stdout");
let mut buf: Vec<u8> = Vec::new();
doc.render_writer(80, &mut buf).unwrap();
assert_eq!(buf, b"written to stdout");

Trait Implementations§

Source§

impl Clone for Doc

Source§

fn clone(&self) -> Doc

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Doc

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Doc

The empty document — same as Doc::nil.

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl Drop for Doc

Source§

fn drop(&mut self)

Dismantle the document iteratively when this is its last owner.

The document is a tree of reference-counted nodes, so the derived drop glue would recurse one call frame per level and overflow the stack on a deeply nested document (a long chain of binary expressions, say). This impl walks a uniquely-owned spine with an explicit heap work list instead, keeping the actual node drops shallow. Leaves and shared nodes take a branch-only fast path that allocates nothing.

Source§

fn pin_drop(self: Pin<&mut Self>)

🔬This is a nightly-only experimental API. (pin_ergonomics)
Execute the destructor for this type, but different to Drop::drop, it requires self to be pinned. Read more
Source§

impl From<&'static str> for Doc

Build a text document from a static string slice, without allocating.

Source§

fn from(s: &'static str) -> Self

Converts to this type from the input type.
Source§

impl From<String> for Doc

Build a text document from an owned string.

Source§

fn from(s: String) -> Self

Converts to this type from the input type.

Auto Trait Implementations§

§

impl !Send for Doc

§

impl !Sync for Doc

§

impl Freeze for Doc

§

impl RefUnwindSafe for Doc

§

impl Unpin for Doc

§

impl UnsafeUnpin for Doc

§

impl UnwindSafe for Doc

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.