don't run pretty.rs tests

This commit is contained in:
Folkert 2020-04-04 01:50:19 +02:00
parent ba16eff24d
commit 51a3c72424
4 changed files with 63 additions and 592 deletions

View file

@ -7,13 +7,8 @@ use roc_types::subs::{Content, Subs};
use roc_types::types::{write_error_type, ErrorType}; use roc_types::types::{write_error_type, ErrorType};
use std::path::PathBuf; use std::path::PathBuf;
// use ven_pretty::termcolor::{Color, ColorChoice, ColorSpec, StandardStream};
use std::fmt; use std::fmt;
use ven_pretty::{ use ven_pretty::{BoxAllocator, DocAllocator, DocBuilder, Render, RenderAnnotated};
BoxAllocator, BoxDoc, DocAllocator, DocBuilder, FmtWrite, Render, RenderAnnotated,
};
type Doc<'a> = DocBuilder<'a, BoxAllocator, Annotation>;
/// A textual report. /// A textual report.
pub struct Report { pub struct Report {
@ -210,10 +205,6 @@ pub fn url(str: &str) -> ReportText {
ReportText::Url(Box::from(str)) ReportText::Url(Box::from(str))
} }
pub fn with_indent(n: usize, report_text: ReportText) -> ReportText {
ReportText::Indent(n, Box::new(report_text))
}
pub const RED_CODE: &str = "\u{001b}[31m"; pub const RED_CODE: &str = "\u{001b}[31m";
pub const WHITE_CODE: &str = "\u{001b}[37m"; pub const WHITE_CODE: &str = "\u{001b}[37m";
pub const BLUE_CODE: &str = "\u{001b}[34m"; pub const BLUE_CODE: &str = "\u{001b}[34m";
@ -228,12 +219,6 @@ pub const UNDERLINE_CODE: &str = "\u{001b}[4m";
pub const RESET_CODE: &str = "\u{001b}[0m"; pub const RESET_CODE: &str = "\u{001b}[0m";
struct CiEnv<'a> {
home: ModuleId,
src_lines: &'a [&'a str],
interns: &'a Interns,
}
pub struct CiWrite<W> { pub struct CiWrite<W> {
style_stack: Vec<Annotation>, style_stack: Vec<Annotation>,
upstream: W, upstream: W,
@ -445,11 +430,11 @@ impl ReportText {
src_lines: &[&str], src_lines: &[&str],
interns: &Interns, interns: &Interns,
) { ) {
let allocator = BoxAllocator; let alloc = BoxAllocator;
let err_msg = "<buffer is not a utf-8 encoded string>"; let err_msg = "<buffer is not a utf-8 encoded string>";
self.pretty::<_>(&allocator, subs, home, src_lines, interns) self.pretty::<_>(&alloc, subs, home, src_lines, interns)
.1 .1
.render_raw(70, &mut CiWrite::new(buf)) .render_raw(70, &mut CiWrite::new(buf))
.expect(err_msg); .expect(err_msg);
@ -465,11 +450,11 @@ impl ReportText {
interns: &Interns, interns: &Interns,
palette: &Palette, palette: &Palette,
) { ) {
let allocator = BoxAllocator; let alloc = BoxAllocator;
let err_msg = "<buffer is not a utf-8 encoded string>"; let err_msg = "<buffer is not a utf-8 encoded string>";
self.pretty::<_>(&allocator, subs, home, src_lines, interns) self.pretty::<_>(&alloc, subs, home, src_lines, interns)
.1 .1
.render_raw(70, &mut ColorWrite::new(palette, buf)) .render_raw(70, &mut ColorWrite::new(palette, buf))
.expect(err_msg); .expect(err_msg);
@ -479,7 +464,7 @@ impl ReportText {
/// monospace font, etc) is done in the CiWrite and ColorWrite `RenderAnnotated` instances. /// monospace font, etc) is done in the CiWrite and ColorWrite `RenderAnnotated` instances.
pub fn pretty<'b, D>( pub fn pretty<'b, D>(
self, self,
allocator: &'b D, alloc: &'b D,
subs: &mut Subs, subs: &mut Subs,
home: ModuleId, home: ModuleId,
src_lines: &'b [&'b str], src_lines: &'b [&'b str],
@ -492,30 +477,30 @@ impl ReportText {
use ReportText::*; use ReportText::*;
match self { match self {
Plain(string) => allocator Plain(string) => alloc
.text(format!("{}", string)) .text(format!("{}", string))
.annotate(Annotation::PlainText), .annotate(Annotation::PlainText),
EmText(string) => allocator EmText(string) => alloc
.text(format!("{}", string)) .text(format!("{}", string))
.annotate(Annotation::Emphasized), .annotate(Annotation::Emphasized),
Url(url) => allocator.text(format!("{}", url)).annotate(Annotation::Url), Url(url) => alloc.text(format!("{}", url)).annotate(Annotation::Url),
Keyword(string) => allocator Keyword(string) => alloc
.text(format!("{}", string)) .text(format!("{}", string))
.annotate(Annotation::Keyword), .annotate(Annotation::Keyword),
GlobalTag(string) => allocator GlobalTag(string) => alloc
.text(format!("{}", string)) .text(format!("{}", string))
.annotate(Annotation::GlobalTag), .annotate(Annotation::GlobalTag),
RecordField(string) => allocator RecordField(string) => alloc
.text(format!(".{}", string)) .text(format!(".{}", string))
.annotate(Annotation::RecordField), .annotate(Annotation::RecordField),
PrivateTag(symbol) => { PrivateTag(symbol) => {
if symbol.module_id() == home { if symbol.module_id() == home {
// Render it unqualified if it's in the current module. // Render it unqualified if it's in the current module.
allocator alloc
.text(format!("{}", symbol.ident_string(interns))) .text(format!("{}", symbol.ident_string(interns)))
.annotate(Annotation::PrivateTag) .annotate(Annotation::PrivateTag)
} else { } else {
allocator alloc
.text(format!( .text(format!(
"{}.{}", "{}.{}",
symbol.module_string(interns), symbol.module_string(interns),
@ -527,11 +512,11 @@ impl ReportText {
Value(symbol) => { Value(symbol) => {
if symbol.module_id() == home { if symbol.module_id() == home {
// Render it unqualified if it's in the current module. // Render it unqualified if it's in the current module.
allocator alloc
.text(format!("{}", symbol.ident_string(interns))) .text(format!("{}", symbol.ident_string(interns)))
.annotate(Annotation::Symbol) .annotate(Annotation::Symbol)
} else { } else {
allocator alloc
.text(format!( .text(format!(
"{}.{}", "{}.{}",
symbol.module_string(interns), symbol.module_string(interns),
@ -541,55 +526,53 @@ impl ReportText {
} }
} }
Module(module_id) => allocator Module(module_id) => alloc
.text(format!("{}", interns.module_name(module_id))) .text(format!("{}", interns.module_name(module_id)))
.annotate(Annotation::Module), .annotate(Annotation::Module),
Type(content) => match content { Type(content) => match content {
Content::FlexVar(_) | Content::RigidVar(_) => allocator Content::FlexVar(_) | Content::RigidVar(_) => alloc
.text(content_to_string(content, subs, home, interns)) .text(content_to_string(content, subs, home, interns))
.annotate(Annotation::TypeVariable), .annotate(Annotation::TypeVariable),
Content::Structure(_) => allocator Content::Structure(_) => alloc
.text(content_to_string(content, subs, home, interns)) .text(content_to_string(content, subs, home, interns))
.annotate(Annotation::Structure), .annotate(Annotation::Structure),
Content::Alias(_, _, _) => allocator Content::Alias(_, _, _) => alloc
.text(content_to_string(content, subs, home, interns)) .text(content_to_string(content, subs, home, interns))
.annotate(Annotation::Alias), .annotate(Annotation::Alias),
Content::Error => allocator.text(content_to_string(content, subs, home, interns)), Content::Error => alloc.text(content_to_string(content, subs, home, interns)),
}, },
ErrorType(error_type) => allocator ErrorType(error_type) => alloc
.nil() .nil()
.append(allocator.hardline()) .append(alloc.hardline())
.append( .append(
allocator alloc
.text(write_error_type(home, interns, error_type)) .text(write_error_type(home, interns, error_type))
.indent(4), .indent(4),
) )
.append(allocator.hardline()), .append(alloc.hardline()),
Indent(n, nested) => { Indent(n, nested) => {
let rest = nested.pretty(allocator, subs, home, src_lines, interns); let rest = nested.pretty(alloc, subs, home, src_lines, interns);
allocator.nil().append(rest).indent(n) alloc.nil().append(rest).indent(n)
} }
Docs(_) => { Docs(_) => {
panic!("TODO implment docs"); panic!("TODO implment docs");
} }
Concat(report_texts) => allocator.concat( Concat(report_texts) => alloc.concat(
report_texts report_texts
.into_iter() .into_iter()
.map(|rep| rep.pretty(allocator, subs, home, src_lines, interns)), .map(|rep| rep.pretty(alloc, subs, home, src_lines, interns)),
), ),
Stack(report_texts) => allocator.intersperse( Stack(report_texts) => alloc.intersperse(
report_texts report_texts
.into_iter() .into_iter()
.map(|rep| (rep.pretty(allocator, subs, home, src_lines, interns))), .map(|rep| (rep.pretty(alloc, subs, home, src_lines, interns))),
allocator.hardline(), alloc.hardline(),
), ),
BinOp(bin_op) => allocator BinOp(bin_op) => alloc.text(bin_op.to_string()).annotate(Annotation::BinOp),
.text(bin_op.to_string())
.annotate(Annotation::BinOp),
Region(region) => { Region(region) => {
let max_line_number_length = (region.end_line + 1).to_string().len(); let max_line_number_length = (region.end_line + 1).to_string().len();
let indent = 2; let indent = 2;
@ -603,42 +586,42 @@ impl ReportText {
let line = src_lines[i as usize]; let line = src_lines[i as usize];
let rest_of_line = if line.trim().is_empty() { let rest_of_line = if line.trim().is_empty() {
allocator.nil() alloc.nil()
} else { } else {
allocator alloc
.nil() .nil()
.append(allocator.text(line).indent(2)) .append(alloc.text(line).indent(2))
.annotate(Annotation::CodeBlock) .annotate(Annotation::CodeBlock)
}; };
let source_line = allocator let source_line = alloc
.line() .line()
.append( .append(
allocator alloc
.text(" ".repeat(max_line_number_length - this_line_number_length)), .text(" ".repeat(max_line_number_length - this_line_number_length)),
) )
.append(allocator.text(line_number).annotate(Annotation::LineNumber)) .append(alloc.text(line_number).annotate(Annotation::LineNumber))
.append(allocator.text("").annotate(Annotation::GutterBar)) .append(alloc.text("").annotate(Annotation::GutterBar))
.append(rest_of_line); .append(rest_of_line);
let highlight_line = allocator let highlight_line = alloc
.line() .line()
.append(allocator.text(" ".repeat(max_line_number_length))) .append(alloc.text(" ".repeat(max_line_number_length)))
.append(allocator.text("").annotate(Annotation::GutterBar)) .append(alloc.text("").annotate(Annotation::GutterBar))
.append( .append(
allocator alloc
.text(" ".repeat(region.start_col as usize)) .text(" ".repeat(region.start_col as usize))
.indent(indent), .indent(indent),
) )
.append( .append(
allocator alloc
.text("^".repeat((region.end_col - region.start_col) as usize)) .text("^".repeat((region.end_col - region.start_col) as usize))
.annotate(Annotation::Error), .annotate(Annotation::Error),
); );
source_line.append(highlight_line) source_line.append(highlight_line)
} else { } else {
let mut result = allocator.nil(); let mut result = alloc.nil();
for i in region.start_line..=region.end_line { for i in region.start_line..=region.end_line {
let line_number_string = (i + 1).to_string(); let line_number_string = (i + 1).to_string();
let line_number = line_number_string; let line_number = line_number_string;
@ -646,24 +629,23 @@ impl ReportText {
let line = src_lines[i as usize]; let line = src_lines[i as usize];
let rest_of_line = if !line.trim().is_empty() { let rest_of_line = if !line.trim().is_empty() {
allocator alloc
.text(line) .text(line)
.annotate(Annotation::CodeBlock) .annotate(Annotation::CodeBlock)
.indent(indent) .indent(indent)
} else { } else {
allocator.nil() alloc.nil()
}; };
let source_line = allocator let source_line =
alloc
.line() .line()
.append( .append(alloc.text(
allocator.text(
" ".repeat(max_line_number_length - this_line_number_length), " ".repeat(max_line_number_length - this_line_number_length),
), ))
) .append(alloc.text(line_number).annotate(Annotation::LineNumber))
.append(allocator.text(line_number).annotate(Annotation::LineNumber)) .append(alloc.text("").annotate(Annotation::GutterBar))
.append(allocator.text("").annotate(Annotation::GutterBar)) .append(alloc.text(">").annotate(Annotation::Error))
.append(allocator.text(">").annotate(Annotation::Error))
.append(rest_of_line); .append(rest_of_line);
result = result.append(source_line); result = result.append(source_line);
@ -671,12 +653,12 @@ impl ReportText {
result result
}; };
allocator alloc
.nil() .nil()
.append(allocator.line()) .append(alloc.line())
.append(body) .append(body)
.append(allocator.line()) .append(alloc.line())
.append(allocator.line()) .append(alloc.line())
} }
} }
} }

View file

@ -1,6 +1,6 @@
use crate::report::{ use crate::report::{
global_tag_text, keyword_text, plain_text, private_tag_text, record_field_text, with_indent, global_tag_text, keyword_text, plain_text, private_tag_text, record_field_text, Report,
Report, ReportText, ReportText,
}; };
use roc_can::expected::{Expected, PExpected}; use roc_can::expected::{Expected, PExpected};
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;

View file

@ -22,14 +22,3 @@ termcolor = { version = "1.1.0", optional = true }
tempfile = "3.1.0" tempfile = "3.1.0"
difference = "2" difference = "2"
criterion = "0.3" criterion = "0.3"
[[example]]
name = "trees"
[[example]]
name = "colored"
required-features = ["termcolor"]
[[bench]]
name = "trees"
harness = false

View file

@ -1,142 +1,3 @@
//! This crate defines a
//! [Wadler-style](http://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf)
//! pretty-printing API.
//!
//! Start with with the static functions of [Doc](enum.Doc.html).
//!
//! ## Quick start
//!
//! Let's pretty-print simple sexps! We want to pretty print sexps like
//!
//! ```lisp
//! (1 2 3)
//! ```
//! or, if the line would be too long, like
//!
//! ```lisp
//! ((1)
//! (2 3)
//! (4 5 6))
//! ```
//!
//! A _simple symbolic expression_ consists of a numeric _atom_ or a nested ordered _list_ of
//! symbolic expression children.
//!
//! ```rust
//! # use pretty::*;
//! enum SExp {
//! Atom(u32),
//! List(Vec<SExp>),
//! }
//! use SExp::*;
//! # fn main() { }
//! ```
//!
//! We define a simple conversion to a [Doc](enum.Doc.html). Atoms are rendered as strings; lists
//! are recursively rendered, with spaces between children where appropriate. Children are
//! [nested]() and [grouped](), allowing them to be laid out in a single line as appropriate.
//!
//! ```rust
//! # use pretty::*;
//! # enum SExp {
//! # Atom(u32),
//! # List(Vec<SExp>),
//! # }
//! # use SExp::*;
//! impl SExp {
//! /// Return a pretty printed format 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.into_iter().map(|x| x.to_doc()), Doc::line()).nest(1).group())
//! .append(RcDoc::text(")"))
//! }
//! }
//! }
//! # fn main() { }
//! ```
//!
//! Next, we convert the [Doc](enum.Doc.html) to a plain old string.
//!
//! ```rust
//! # use pretty::*;
//! # enum SExp {
//! # Atom(u32),
//! # List(Vec<SExp>),
//! # }
//! # use SExp::*;
//! # impl SExp {
//! # /// Return a pretty printed format of self.
//! # pub fn to_doc(&self) -> BoxDoc<()> {
//! # match *self {
//! # Atom(ref x) => BoxDoc::as_string(x),
//! # List(ref xs) =>
//! # BoxDoc::text("(")
//! # .append(BoxDoc::intersperse(xs.into_iter().map(|x| x.to_doc()), Doc::line()).nest(1).group())
//! # .append(BoxDoc::text(")"))
//! # }
//! # }
//! # }
//! impl SExp {
//! pub fn to_pretty(&self, width: usize) -> String {
//! let mut w = Vec::new();
//! self.to_doc().render(width, &mut w).unwrap();
//! String::from_utf8(w).unwrap()
//! }
//! }
//! # fn main() { }
//! ```
//!
//! And finally we can test that the nesting and grouping behaves as we expected.
//!
//! ```rust
//! # use pretty::*;
//! # enum SExp {
//! # Atom(u32),
//! # List(Vec<SExp>),
//! # }
//! # use SExp::*;
//! # impl SExp {
//! # /// Return a pretty printed format of self.
//! # pub fn to_doc(&self) -> BoxDoc<()> {
//! # match *self {
//! # Atom(ref x) => BoxDoc::as_string(x),
//! # List(ref xs) =>
//! # BoxDoc::text("(")
//! # .append(BoxDoc::intersperse(xs.into_iter().map(|x| x.to_doc()), Doc::line()).nest(1).group())
//! # .append(BoxDoc::text(")"))
//! # }
//! # }
//! # }
//! # impl SExp {
//! # pub fn to_pretty(&self, width: usize) -> String {
//! # let mut w = Vec::new();
//! # self.to_doc().render(width, &mut w).unwrap();
//! # String::from_utf8(w).unwrap()
//! # }
//! # }
//! # fn main() {
//! let atom = SExp::Atom(5);
//! assert_eq!("5", atom.to_pretty(10));
//! let list = SExp::List(vec![SExp::Atom(1), SExp::Atom(2), SExp::Atom(3)]);
//! assert_eq!("(1 2 3)", list.to_pretty(10));
//! assert_eq!("\
//! (1
//! 2
//! 3)", list.to_pretty(5));
//! # }
//! ```
//!
//! ## Advanced usage
//!
//! There's a more efficient pattern that uses the [DocAllocator](trait.DocAllocator.html) trait, as
//! implemented by [BoxAllocator](struct.BoxAllocator.html), to allocate
//! [DocBuilder](struct.DocBuilder.html) instances. See
//! [examples/trees.rs](https://github.com/freebroccolo/pretty.rs/blob/master/examples/trees.rs#L39)
//! for this approach.
#[cfg(feature = "termcolor")] #[cfg(feature = "termcolor")]
pub extern crate termcolor; pub extern crate termcolor;
@ -547,13 +408,6 @@ where
/// Returns a value which implements `std::fmt::Display` /// 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] #[inline]
pub fn pretty<'d>(&'d self, width: usize) -> Pretty<'a, 'd, T, A> { pub fn pretty<'d>(&'d self, width: usize) -> Pretty<'a, 'd, T, A> {
Pretty { doc: self, width } Pretty { doc: self, width }
@ -664,24 +518,6 @@ where
/// Acts like `line` but behaves like `nil` if grouped on a single line /// 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] #[inline]
fn line_(&'a self) -> DocBuilder<'a, Self, A> { fn line_(&'a self) -> DocBuilder<'a, Self, A> {
self.hardline().flat_alt(self.nil()) self.hardline().flat_alt(self.nil())
@ -756,16 +592,6 @@ where
/// Allocate a document that acts differently based on the position and page layout /// 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] #[inline]
fn column(&'a self, f: impl Fn(usize) -> Self::Doc + 'a) -> DocBuilder<'a, Self, A> { 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()) DocBuilder(self, Doc::Column(self.alloc_column_fn(f)).into())
@ -773,16 +599,6 @@ where
/// Allocate a document that acts differently based on the current nesting level /// 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] #[inline]
fn nesting(&'a self, f: impl Fn(usize) -> Self::Doc + 'a) -> DocBuilder<'a, Self, A> { 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()) DocBuilder(self, Doc::Nesting(self.alloc_column_fn(f)).into())
@ -882,28 +698,6 @@ where
/// Acts as `self` when laid out on multiple lines and acts as `that` when laid out on a single line. /// Acts as `self` when laid out on multiple lines and acts as `that` when laid out on a single line.
/// ///
/// ```
/// use pretty::{Arena, DocAllocator};
///
/// let arena = Arena::<()>::new();
/// let body = arena.line().append("x");
/// let doc = arena.text("let")
/// .append(arena.line())
/// .append("x")
/// .group()
/// .append(
/// body.clone()
/// .flat_alt(
/// arena.line()
/// .append("in")
/// .append(body)
/// )
/// )
/// .group();
///
/// assert_eq!(doc.1.pretty(100).to_string(), "let x in x");
/// assert_eq!(doc.1.pretty(8).to_string(), "let x\nx");
/// ```
#[inline] #[inline]
pub fn flat_alt<E>(self, that: E) -> DocBuilder<'a, D, A> pub fn flat_alt<E>(self, that: E) -> DocBuilder<'a, D, A>
where where
@ -970,14 +764,6 @@ where
/// NOTE: The doc pointer type, `D` may need to be cloned. Consider using cheaply cloneable ptr /// NOTE: The doc pointer type, `D` may need to be cloned. Consider using cheaply cloneable ptr
/// like `RefDoc` or `RcDoc` /// like `RefDoc` or `RcDoc`
/// ///
/// ```rust
/// use pretty::DocAllocator;
///
/// let arena = pretty::Arena::<()>::new();
/// let doc = arena.text("lorem").append(arena.text(" "))
/// .append(arena.intersperse(["ipsum", "dolor"].iter().cloned(), arena.line_()).align());
/// assert_eq!(doc.1.pretty(80).to_string(), "lorem ipsum\n dolor");
/// ```
#[inline] #[inline]
pub fn align(self) -> DocBuilder<'a, D, A> pub fn align(self) -> DocBuilder<'a, D, A>
where where
@ -997,17 +783,6 @@ where
/// NOTE: The doc pointer type, `D` may need to be cloned. Consider using cheaply cloneable ptr /// NOTE: The doc pointer type, `D` may need to be cloned. Consider using cheaply cloneable ptr
/// like `RefDoc` or `RcDoc` /// like `RefDoc` or `RcDoc`
/// ///
/// ```rust
/// use pretty::DocAllocator;
///
/// let arena = pretty::Arena::<()>::new();
/// let doc = arena.text("prefix").append(arena.text(" "))
/// .append(arena.reflow("Indenting these words with nest").hang(4));
/// assert_eq!(
/// doc.1.pretty(24).to_string(),
/// "prefix Indenting these\n words with\n nest",
/// );
/// ```
#[inline] #[inline]
pub fn hang(self, adjust: isize) -> DocBuilder<'a, D, A> pub fn hang(self, adjust: isize) -> DocBuilder<'a, D, A>
where where
@ -1021,21 +796,6 @@ where
/// NOTE: The doc pointer type, `D` may need to be cloned. Consider using cheaply cloneable ptr /// NOTE: The doc pointer type, `D` may need to be cloned. Consider using cheaply cloneable ptr
/// like `RefDoc` or `RcDoc` /// like `RefDoc` or `RcDoc`
/// ///
/// ```rust
/// use pretty::DocAllocator;
///
/// let arena = pretty::Arena::<()>::new();
/// let doc = arena.text("prefix").append(arena.text(" "))
/// .append(arena.reflow("The indent function indents these words!").indent(4));
/// assert_eq!(
/// doc.1.pretty(24).to_string(),
/// "
/// prefix The indent
/// function
/// indents these
/// words!".trim_start(),
/// );
/// ```
#[inline] #[inline]
pub fn indent(self, adjust: usize) -> DocBuilder<'a, D, A> pub fn indent(self, adjust: usize) -> DocBuilder<'a, D, A>
where where
@ -1061,16 +821,6 @@ where
/// NOTE: The doc pointer type, `D` may need to be cloned. Consider using cheaply cloneable ptr /// NOTE: The doc pointer type, `D` may need to be cloned. Consider using cheaply cloneable ptr
/// like `RefDoc` or `RcDoc` /// like `RefDoc` or `RcDoc`
/// ///
/// ```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] #[inline]
pub fn width(self, f: impl Fn(isize) -> D::Doc + 'a) -> DocBuilder<'a, D, A> pub fn width(self, f: impl Fn(isize) -> D::Doc + 'a) -> DocBuilder<'a, D, A>
where where
@ -1271,253 +1021,3 @@ impl<'a, A> DocAllocator<'a, A> for Arena<'a, A> {
self.alloc_any(f) self.alloc_any(f)
} }
} }
#[cfg(test)]
mod tests {
use difference;
use super::*;
#[cfg(target_pointer_width = "64")]
#[test]
fn doc_size() {
// Safeguard against accidentally growing Doc
assert_eq!(8 * 3, std::mem::size_of::<Doc<RefDoc>>());
}
macro_rules! test {
($size:expr, $actual:expr, $expected:expr) => {
let mut s = String::new();
$actual.render_fmt($size, &mut s).unwrap();
difference::assert_diff!(&s, $expected, "\n", 0);
};
($actual:expr, $expected:expr) => {
test!(70, $actual, $expected)
};
}
#[test]
fn box_doc_inference() {
let doc: BoxDoc<()> = BoxDoc::group(
BoxDoc::text("test")
.append(BoxDoc::line())
.append(BoxDoc::text("test")),
);
test!(doc, "test test");
}
#[test]
fn newline_in_text() {
let doc: BoxDoc<()> = BoxDoc::group(
BoxDoc::text("test").append(
BoxDoc::line()
.append(BoxDoc::text("\"test\n test\""))
.nest(4),
),
);
test!(5, doc, "test\n \"test\n test\"");
}
#[test]
fn forced_newline() {
let doc: BoxDoc<()> = BoxDoc::group(
BoxDoc::text("test")
.append(BoxDoc::hardline())
.append(BoxDoc::text("test")),
);
test!(doc, "test\ntest");
}
#[test]
fn space_do_not_reset_pos() {
let doc: BoxDoc<()> = BoxDoc::group(BoxDoc::text("test").append(BoxDoc::line()))
.append(BoxDoc::text("test"))
.append(BoxDoc::group(BoxDoc::line()).append(BoxDoc::text("test")));
test!(9, doc, "test test\ntest");
}
// Tests that the `BoxDoc::hardline()` does not cause the rest of document to think that it fits on
// a single line but instead breaks on the `BoxDoc::line()` to fit with 6 columns
#[test]
fn newline_does_not_cause_next_line_to_be_to_long() {
let doc: RcDoc<()> = RcDoc::group(
RcDoc::text("test").append(RcDoc::hardline()).append(
RcDoc::text("test")
.append(RcDoc::line())
.append(RcDoc::text("test")),
),
);
test!(6, doc, "test\ntest\ntest");
}
#[test]
fn newline_after_group_does_not_affect_it() {
let arena = Arena::<()>::new();
let doc = arena.text("x").append(arena.line()).append("y").group();
test!(100, doc.append(arena.hardline()).1, "x y\n");
}
#[test]
fn block() {
let doc: RcDoc<()> = RcDoc::group(
RcDoc::text("{")
.append(
RcDoc::line()
.append(RcDoc::text("test"))
.append(RcDoc::line())
.append(RcDoc::text("test"))
.nest(2),
)
.append(RcDoc::line())
.append(RcDoc::text("}")),
);
test!(5, doc, "{\n test\n test\n}");
}
#[test]
fn line_comment() {
let doc: BoxDoc<()> = BoxDoc::group(
BoxDoc::text("{")
.append(
BoxDoc::line()
.append(BoxDoc::text("test"))
.append(BoxDoc::line())
.append(BoxDoc::text("// a").append(BoxDoc::hardline()))
.append(BoxDoc::text("test"))
.nest(2),
)
.append(BoxDoc::line())
.append(BoxDoc::text("}")),
);
test!(14, doc, "{\n test\n // a\n test\n}");
}
#[test]
fn annotation_no_panic() {
let doc: BoxDoc<()> = BoxDoc::group(
BoxDoc::text("test")
.annotate(())
.append(BoxDoc::hardline())
.annotate(())
.append(BoxDoc::text("test")),
);
test!(doc, "test\ntest");
}
#[test]
fn union() {
let arg: BoxDoc<()> = BoxDoc::text("(");
let tuple = |line: BoxDoc<'static, ()>| {
line.append(BoxDoc::text("x").append(",").group())
.append(BoxDoc::line())
.append(BoxDoc::text("1234567890").append(",").group())
.nest(2)
.append(BoxDoc::line_())
.append(")")
};
let from = BoxDoc::text("let")
.append(BoxDoc::line())
.append(BoxDoc::text("x"))
.append(BoxDoc::line())
.append(BoxDoc::text("="))
.group();
let single = from
.clone()
.append(BoxDoc::line())
.append(arg.clone())
.group()
.append(tuple(BoxDoc::line_()))
.group();
let hang = from
.clone()
.append(BoxDoc::line())
.append(arg.clone())
.group()
.append(tuple(BoxDoc::hardline()))
.group();
let break_all = from
.append(BoxDoc::line())
.append(arg.clone())
.append(tuple(BoxDoc::line()))
.group()
.nest(2);
let doc = BoxDoc::group(single.union(hang.union(break_all)));
test!(doc, "let x = (x, 1234567890,)");
test!(8, doc, "let x =\n (\n x,\n 1234567890,\n )");
test!(14, doc, "let x = (\n x,\n 1234567890,\n)");
}
#[test]
fn usize_max_value() {
let doc: BoxDoc<()> = BoxDoc::group(
BoxDoc::text("test")
.append(BoxDoc::line())
.append(BoxDoc::text("test")),
);
test!(usize::max_value(), doc, "test test");
}
pub struct TestWriter<W> {
upstream: W,
}
impl<W> TestWriter<W> {
pub fn new(upstream: W) -> Self {
Self { upstream }
}
}
impl<W> Render for TestWriter<W>
where
W: Render,
{
type Error = W::Error;
fn write_str(&mut self, s: &str) -> Result<usize, W::Error> {
self.upstream.write_str(s)
}
fn write_str_all(&mut self, s: &str) -> Result<(), W::Error> {
self.upstream.write_str_all(s)
}
}
impl<W> RenderAnnotated<()> for TestWriter<W>
where
W: Render,
{
fn push_annotation(&mut self, _: &()) -> Result<(), Self::Error> {
self.upstream.write_str_all("[")
}
fn pop_annotation(&mut self) -> Result<(), Self::Error> {
self.upstream.write_str_all("]")
}
}
#[test]
fn annotations() {
let actual = BoxDoc::text("abc").annotate(()).annotate(());
let mut s = String::new();
actual
.render_raw(70, &mut TestWriter::new(FmtWrite::new(&mut s)))
.unwrap();
difference::assert_diff!(&s, "[[abc]]", "\n", 0);
}
}