Skip to content
/ elegance Public

A pretty-printing library for Rust with a focus on speed and compactness.

License

Notifications You must be signed in to change notification settings

Wybxc/elegance

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

34 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Elegance

GitHub Actions Workflow Status docs.rs

A pretty-printing library for Rust with a focus on speed and compactness.

Usage

Create a printer:

let mut pp = Printer::new(String::new(), 40);

Add some text and spaces:

pp.text("Hello, world!")?;
pp.space()?; // breakable space
pp.hard_break()?; // forced line break

Enclose structures in groups:

pp.group(2, |pp| {
    pp.text("foo")?;
    pp.space()?;
    pp.text("bar")
})?;

Finish the document:

let result = pp.finish()?;
println!("{}", result);

Streaming output

The printer can write to any std::io::Write implementation.

use elegant::{Printer, Io};
let mut pp = Printer::new(Io(std::io::stdout()), 40);

Examples

use elegance::*;

enum SExp {
    Atom(u32),
    List(Vec<SExp>),
}

impl SExp {
    pub fn print<R: Render>(&self, pp: &mut Printer<R>) -> Result<(), R::Error> {
        match self {
            SExp::Atom(x) => pp.text_owned(format!("{}", x))?,
            SExp::List(xs) => pp.cgroup(1, |pp| {
                pp.text("(")?;
                if let Some((first, rest)) = xs.split_first() {
                    first.print(pp)?;
                    for v in rest {
                        pp.space()?;
                        v.print(pp)?;
                    }
                }
                pp.text(")")
            })?,
        }
        Ok(())
    }
}

fn main() {
    let exp = SExp::List(vec![
        SExp::List(vec![SExp::Atom(1)]),
        SExp::List(vec![SExp::Atom(2), SExp::Atom(3)]),
        SExp::List(vec![SExp::Atom(4), SExp::Atom(5), SExp::Atom(6)]),
    ]);

    let mut printer = Printer::new(String::new(), 10);
    exp.print(&mut printer).unwrap();
    let result = printer.finish().unwrap();

    assert_eq!(result, indoc::indoc! {"
        ((1)
         (2 3)
         (4 5 6))"});
}

Features

  • unicode-width: Calculate the width of Unicode characters correctly. This feature is enabled by default.

Differences from other libraries

This crate implements an Oppen-style pretty-printing library, while the pretty crate follows a Wadler-style approach.

In Wadler-style pretty-printing, documents are constructed using a composable Doc type and combinators. Here's an example:

impl SExp {
    /// Returns a pretty printed representation of `self`.
    pub fn to_doc(&self) -> RcDoc<()> {
        match *self {
            Atom(ref x) => RcDoc::as_string(x),
            List(ref xs) =>
                RcDoc::text("(")
                    .append(RcDoc::intersperse(xs.iter().map(|x| x.to_doc()), Doc::line()).nest(1).group())
                    .append(RcDoc::text(")"))
        }
    }
}

This method is particularly suitable for functional programming languages but may not be ideal for Rust. Converting a syntax tree into a Doc requires additional memory allocation proportional to the size of the entire document.

The key difference with this library is that it represents the structure of the printed document through control flow rather than data structures. As a result, the printing process is fully streamed and operates within a constant memory footprint.

References

About

A pretty-printing library for Rust with a focus on speed and compactness.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •  

Languages