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

View file

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

View file

@ -22,14 +22,3 @@ termcolor = { version = "1.1.0", optional = true }
tempfile = "3.1.0"
difference = "2"
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")]
pub extern crate termcolor;
@ -547,13 +408,6 @@ where
/// 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) -> Pretty<'a, 'd, T, A> {
Pretty { doc: self, width }
@ -664,24 +518,6 @@ where
/// 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())
@ -756,16 +592,6 @@ where
/// 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())
@ -773,16 +599,6 @@ where
/// 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())
@ -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.
///
/// ```
/// 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]
pub fn flat_alt<E>(self, that: E) -> DocBuilder<'a, D, A>
where
@ -970,14 +764,6 @@ where
/// NOTE: The doc pointer type, `D` may need to be cloned. Consider using cheaply cloneable ptr
/// 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]
pub fn align(self) -> DocBuilder<'a, D, A>
where
@ -997,17 +783,6 @@ where
/// NOTE: The doc pointer type, `D` may need to be cloned. Consider using cheaply cloneable ptr
/// 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]
pub fn hang(self, adjust: isize) -> DocBuilder<'a, D, A>
where
@ -1021,21 +796,6 @@ where
/// NOTE: The doc pointer type, `D` may need to be cloned. Consider using cheaply cloneable ptr
/// 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]
pub fn indent(self, adjust: usize) -> DocBuilder<'a, D, A>
where
@ -1061,16 +821,6 @@ where
/// NOTE: The doc pointer type, `D` may need to be cloned. Consider using cheaply cloneable ptr
/// 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]
pub fn width(self, f: impl Fn(isize) -> D::Doc + 'a) -> DocBuilder<'a, D, A>
where
@ -1271,253 +1021,3 @@ impl<'a, A> DocAllocator<'a, A> for Arena<'a, A> {
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);
}
}