From ba08cedcdfe2ce117d757ab5bc0fcfb4d2a7a6b6 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Sat, 16 Jun 2018 09:34:54 +0200 Subject: [PATCH] feat: Change the type parameter order so attributes can be defaulted Makes it easier to use "un-annotated" documents in many places as the attribute type can just be omitted and it will default to `()` BREAKING CHANGE Swap the type parameter order of any documents --- examples/trees.rs | 10 ++--- src/doc.rs | 42 +++++++++--------- src/lib.rs | 108 +++++++++++++++++++++++----------------------- 3 files changed, 80 insertions(+), 80 deletions(-) diff --git a/examples/trees.rs b/examples/trees.rs index ce08771..59472db 100644 --- a/examples/trees.rs +++ b/examples/trees.rs @@ -16,7 +16,7 @@ impl<'a> Forest<'a> { Forest(&[]) } - fn bracket<'b, A, D>(&'b self, allocator: &'b D) -> DocBuilder<'b, A, D> + fn bracket<'b, D, A>(&'b self, allocator: &'b D) -> DocBuilder<'b, D, A> where D: DocAllocator<'b, A>, D::Doc: Clone, @@ -33,7 +33,7 @@ impl<'a> Forest<'a> { } } - fn pretty<'b, A, D>(&'b self, allocator: &'b D) -> DocBuilder<'b, A, D> + fn pretty<'b, D, A>(&'b self, allocator: &'b D) -> DocBuilder<'b, D, A> where D: DocAllocator<'b, A>, D::Doc: Clone, @@ -69,7 +69,7 @@ impl<'a> Tree<'a> { } } - pub fn pretty<'b, A, D>(&'b self, allocator: &'b D) -> DocBuilder<'b, A, D> + pub fn pretty<'b, D, A>(&'b self, allocator: &'b D) -> DocBuilder<'b, D, A> where D: DocAllocator<'b, A>, D::Doc: Clone, @@ -100,13 +100,13 @@ pub fn main() { { print!("\nwriting to stdout directly:\n"); let mut out = io::stdout(); - example.pretty::<(), _>(&allocator).1.render(70, &mut out) + example.pretty::<_, ()>(&allocator).1.render(70, &mut out) // try writing to memory }.and_then(|()| { print!("\nwriting to string then printing:\n"); let mut mem = Vec::new(); example - .pretty::<(), _>(&allocator) + .pretty::<_, ()>(&allocator) .1 .render(70, &mut mem) // print to console from memory diff --git a/src/doc.rs b/src/doc.rs index 6a53bf5..bc573c7 100644 --- a/src/doc.rs +++ b/src/doc.rs @@ -21,7 +21,7 @@ enum Mode { /// The `B` parameter is used to abstract over pointers to `Doc`. See `RefDoc` and `BoxDoc` for how /// it is used #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub enum Doc<'a, A, B> { +pub enum Doc<'a, B, A = ()> { Nil, Append(B, B), Group(B), @@ -32,11 +32,11 @@ pub enum Doc<'a, A, B> { Annotated(A, B), } -impl<'a, A, B, S> From for Doc<'a, A, B> +impl<'a, B, A, S> From for Doc<'a, B, A> where S: Into>, { - fn from(s: S) -> Doc<'a, A, B> { + fn from(s: S) -> Doc<'a, B, A> { Doc::Text(s.into()) } } @@ -151,30 +151,30 @@ where } } -pub struct Pretty<'a, A, D> +pub struct Pretty<'a, D, A> where A: 'a, D: 'a, { - doc: &'a Doc<'a, A, D>, + doc: &'a Doc<'a, D, A>, width: usize, } -impl<'a, A, D> fmt::Display for Pretty<'a, A, D> +impl<'a, D, A> fmt::Display for Pretty<'a, D, A> where - D: Deref>, + D: Deref>, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.doc.render_fmt(self.width, f) } } -impl<'a, A, B> Doc<'a, A, B> { +impl<'a, B, A> Doc<'a, B, A> { /// Writes a rendered document to a `std::io::Write` object. #[inline] pub fn render<'b, W>(&'b self, width: usize, out: &mut W) -> io::Result<()> where - B: Deref>, + B: Deref>, W: ?Sized + io::Write, { best(self, width, &mut IoWrite(out)) @@ -184,7 +184,7 @@ impl<'a, A, B> Doc<'a, A, B> { #[inline] pub fn render_fmt<'b, W>(&'b self, width: usize, out: &mut W) -> fmt::Result where - B: Deref>, + B: Deref>, W: ?Sized + fmt::Write, { best(self, width, &mut FmtWrite(out)) @@ -194,15 +194,15 @@ impl<'a, A, B> Doc<'a, A, B> { /// /// ``` /// use pretty::Doc; - /// let doc = Doc::<(), _>::group( + /// let doc = Doc::<_>::group( /// Doc::text("hello").append(Doc::space()).append(Doc::text("world")) /// ); /// assert_eq!(format!("{}", doc.pretty(80)), "hello world"); /// ``` #[inline] - pub fn pretty<'b>(&'b self, width: usize) -> Pretty<'b, A, B> + pub fn pretty<'b>(&'b self, width: usize) -> Pretty<'b, B, A> where - B: Deref>, + B: Deref>, { Pretty { doc: self, @@ -230,7 +230,7 @@ impl<'a, B> Doc<'a, ColorSpec, B> { } } -type Cmd<'a, A, B> = (usize, Mode, &'a Doc<'a, A, B>); +type Cmd<'a, B, A> = (usize, Mode, &'a Doc<'a, B, A>); fn write_newline(ind: usize, out: &mut W) -> Result<(), W::Error> where @@ -262,14 +262,14 @@ where } #[inline] -fn fitting<'a, A, B>( - next: Cmd<'a, A, B>, - bcmds: &Vec>, - fcmds: &mut Vec>, +fn fitting<'a, B, A>( + next: Cmd<'a, B, A>, + bcmds: &Vec>, + fcmds: &mut Vec>, mut rem: isize, ) -> bool where - B: Deref>, + B: Deref>, { let mut bidx = bcmds.len(); fcmds.clear(); // clear from previous calls from best @@ -327,9 +327,9 @@ where } #[inline] -fn best<'a, W, A, B>(doc: &'a Doc<'a, A, B>, width: usize, out: &mut W) -> Result<(), W::Error> +fn best<'a, W, B, A>(doc: &'a Doc<'a, B, A>, width: usize, out: &mut W) -> Result<(), W::Error> where - B: Deref>, + B: Deref>, W: ?Sized + RenderAnnotated, { let mut pos = 0usize; diff --git a/src/lib.rs b/src/lib.rs index eefa203..496df38 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,7 +47,7 @@ //! # use SExp::*; //! impl SExp { //! /// Return a pretty printed format of self. -//! pub fn to_doc(&self) -> Doc<(), BoxDoc<()>> { +//! pub fn to_doc(&self) -> Doc> { //! match self { //! &Atom(x) => Doc::as_string(x), //! &List(ref xs) => @@ -72,7 +72,7 @@ //! # use SExp::*; //! # impl SExp { //! # /// Return a pretty printed format of self. -//! # pub fn to_doc(&self) -> Doc<(), BoxDoc<()>> { +//! # pub fn to_doc(&self) -> Doc> { //! # match self { //! # &Atom(x) => Doc::as_string(x), //! # &List(ref xs) => @@ -104,7 +104,7 @@ //! # use SExp::*; //! # impl SExp { //! # /// Return a pretty printed format of self. -//! # pub fn to_doc(&self) -> Doc<(), BoxDoc<()>> { +//! # pub fn to_doc(&self) -> Doc> { //! # match self { //! # &Atom(x) => Doc::as_string(x), //! # &List(ref xs) => @@ -153,7 +153,7 @@ use std::ops::Deref; mod doc; #[derive(Clone, Eq, Ord, PartialEq, PartialOrd)] -pub struct BoxDoc<'a, A>(Box>>); +pub struct BoxDoc<'a, A>(Box, A>>); impl<'a, A> fmt::Debug for BoxDoc<'a, A> where @@ -165,13 +165,13 @@ where } impl<'a, A> BoxDoc<'a, A> { - fn new(doc: doc::Doc<'a, A, BoxDoc<'a, A>>) -> BoxDoc<'a, A> { + fn new(doc: doc::Doc<'a, BoxDoc<'a, A>, A>) -> BoxDoc<'a, A> { BoxDoc(Box::new(doc)) } } impl<'a, A> Deref for BoxDoc<'a, A> { - type Target = doc::Doc<'a, A, BoxDoc<'a, A>>; + type Target = doc::Doc<'a, BoxDoc<'a, A>, A>; fn deref(&self) -> &Self::Target { &self.0 @@ -181,11 +181,11 @@ impl<'a, A> Deref for BoxDoc<'a, A> { /// The `DocBuilder` type allows for convenient appending of documents even for arena allocated /// documents by storing the arena inline. #[derive(Eq, Ord, PartialEq, PartialOrd)] -pub struct DocBuilder<'a, A, D: ?Sized>(pub &'a D, pub doc::Doc<'a, A, D::Doc>) +pub struct DocBuilder<'a, D: ?Sized, A = ()>(pub &'a D, pub doc::Doc<'a, D::Doc, A>) where D: DocAllocator<'a, A> + 'a; -impl<'a, A, D: DocAllocator<'a, A> + 'a> Clone for DocBuilder<'a, A, D> +impl<'a, A, D: DocAllocator<'a, A> + 'a> Clone for DocBuilder<'a, D, A> where A: Clone, D::Doc: Clone, @@ -195,36 +195,36 @@ where } } -impl<'a, A, D: ?Sized> Into> for DocBuilder<'a, A, D> +impl<'a, D: ?Sized, A> Into> for DocBuilder<'a, D, A> where D: DocAllocator<'a, A>, { - fn into(self) -> doc::Doc<'a, A, D::Doc> { + fn into(self) -> doc::Doc<'a, D::Doc, A> { self.1 } } /// The `DocAllocator` trait abstracts over a type which can allocate (pointers to) `Doc`. pub trait DocAllocator<'a, A> { - type Doc: Deref>; + type Doc: Deref>; - fn alloc(&'a self, doc::Doc<'a, A, Self::Doc>) -> Self::Doc; + fn alloc(&'a self, doc::Doc<'a, Self::Doc, A>) -> Self::Doc; /// Allocate an empty document. #[inline] - fn nil(&'a self) -> DocBuilder<'a, A, Self> { + fn nil(&'a self) -> DocBuilder<'a, Self, A> { DocBuilder(self, Nil) } /// Allocate a single newline. #[inline] - fn newline(&'a self) -> DocBuilder<'a, A, Self> { + fn newline(&'a self) -> DocBuilder<'a, Self, A> { DocBuilder(self, Newline) } /// Allocate a single space. #[inline] - fn space(&'a self) -> DocBuilder<'a, A, Self> { + fn space(&'a self) -> DocBuilder<'a, Self, A> { DocBuilder(self, Space) } @@ -232,7 +232,7 @@ pub trait DocAllocator<'a, A> { /// /// The given text must not contain line breaks. #[inline] - fn as_string(&'a self, t: T) -> DocBuilder<'a, A, Self> { + fn as_string(&'a self, t: T) -> DocBuilder<'a, Self, A> { self.text(t.to_string()) } @@ -240,7 +240,7 @@ pub trait DocAllocator<'a, A> { /// /// The given text must not contain line breaks. #[inline] - fn text>>(&'a self, data: T) -> DocBuilder<'a, A, Self> { + fn text>>(&'a self, data: T) -> DocBuilder<'a, Self, A> { let text = data.into(); debug_assert!(!text.contains(|c: char| c == '\n' || c == '\r')); DocBuilder(self, Text(text)) @@ -248,10 +248,10 @@ pub trait DocAllocator<'a, A> { /// Allocate a document concatenating the given documents. #[inline] - fn concat(&'a self, docs: I) -> DocBuilder<'a, A, Self> + fn concat(&'a self, docs: I) -> DocBuilder<'a, Self, A> where I: IntoIterator, - I::Item: Into>, + I::Item: Into>, { docs.into_iter().fold(self.nil(), |a, b| a.append(b)) } @@ -261,11 +261,11 @@ pub trait DocAllocator<'a, A> { /// /// Compare [the `intersperse` method from the `itertools` crate](https://docs.rs/itertools/0.5.9/itertools/trait.Itertools.html#method.intersperse). #[inline] - fn intersperse(&'a self, docs: I, separator: S) -> DocBuilder<'a, A, Self> + fn intersperse(&'a self, docs: I, separator: S) -> DocBuilder<'a, Self, A> where I: IntoIterator, - I::Item: Into>, - S: Into> + Clone, + I::Item: Into>, + S: Into> + Clone, { let mut result = self.nil(); let mut iter = docs.into_iter(); @@ -280,15 +280,15 @@ pub trait DocAllocator<'a, A> { } } -impl<'a, 's, A, D: ?Sized> DocBuilder<'a, A, D> +impl<'a, 's, D: ?Sized, A> DocBuilder<'a, D, A> where D: DocAllocator<'a, A>, { /// Append the given document after this document. #[inline] - pub fn append(self, that: B) -> DocBuilder<'a, A, D> + pub fn append(self, that: B) -> DocBuilder<'a, D, A> where - B: Into>, + B: Into>, { let DocBuilder(allocator, this) = self; let that = that.into(); @@ -307,14 +307,14 @@ where /// horizontally and combined into a one single line, or they are each layed out on their own /// line. #[inline] - pub fn group(self) -> DocBuilder<'a, A, D> { + pub fn group(self) -> DocBuilder<'a, D, A> { let DocBuilder(allocator, this) = self; DocBuilder(allocator, Group(allocator.alloc(this))) } /// Increase the indentation level of this document. #[inline] - pub fn nest(self, offset: usize) -> DocBuilder<'a, A, D> { + pub fn nest(self, offset: usize) -> DocBuilder<'a, D, A> { if offset == 0 { return self; } @@ -323,7 +323,7 @@ where } #[inline] - pub fn annotate(self, ann: A) -> DocBuilder<'a, A, D> { + pub fn annotate(self, ann: A) -> DocBuilder<'a, D, A> { let DocBuilder(allocator, this) = self; DocBuilder(allocator, Annotated(ann, allocator.alloc(this))) } @@ -331,7 +331,7 @@ where /// Newtype wrapper for `&doc::Doc` #[derive(Clone, Eq, Ord, PartialEq, PartialOrd)] -pub struct RefDoc<'a, A: 'a>(&'a doc::Doc<'a, A, RefDoc<'a, A>>); +pub struct RefDoc<'a, A: 'a>(&'a doc::Doc<'a, RefDoc<'a, A>, A>); impl<'a, A> fmt::Debug for RefDoc<'a, A> where @@ -343,24 +343,24 @@ where } impl<'a, A> Deref for RefDoc<'a, A> { - type Target = doc::Doc<'a, A, RefDoc<'a, A>>; + type Target = doc::Doc<'a, RefDoc<'a, A>, A>; - fn deref(&self) -> &doc::Doc<'a, A, RefDoc<'a, A>> { + fn deref(&self) -> &Self::Target { &self.0 } } /// An arena which can be used to allocate `Doc` values. -pub type Arena<'a, A> = typed_arena::Arena>>; +pub type Arena<'a, A> = typed_arena::Arena, A>>; -impl<'a, A, D> DocAllocator<'a, A> for &'a D +impl<'a, D, A> DocAllocator<'a, A> for &'a D where D: ?Sized + DocAllocator<'a, A>, { type Doc = D::Doc; #[inline] - fn alloc(&'a self, doc: doc::Doc<'a, A, Self::Doc>) -> Self::Doc { + fn alloc(&'a self, doc: doc::Doc<'a, Self::Doc, A>) -> Self::Doc { (**self).alloc(doc) } } @@ -369,7 +369,7 @@ impl<'a, A> DocAllocator<'a, A> for Arena<'a, A> { type Doc = RefDoc<'a, A>; #[inline] - fn alloc(&'a self, doc: doc::Doc<'a, A, Self::Doc>) -> Self::Doc { + fn alloc(&'a self, doc: doc::Doc<'a, Self::Doc, A>) -> Self::Doc { RefDoc(match doc { Space => &Doc::Space, Newline => &Doc::Newline, @@ -386,7 +386,7 @@ impl<'a, A> DocAllocator<'a, A> for BoxAllocator { type Doc = BoxDoc<'a, A>; #[inline] - fn alloc(&'a self, doc: doc::Doc<'a, A, Self::Doc>) -> Self::Doc { + fn alloc(&'a self, doc: doc::Doc<'a, Self::Doc, A>) -> Self::Doc { BoxDoc::new(doc) } } @@ -429,18 +429,18 @@ impl<'a, A, B> Doc<'a, A, B> { } } -impl<'a, A> Doc<'a, A, BoxDoc<'a, A>> { +impl<'a, A> Doc<'a, BoxDoc<'a, A>, A> { /// Append the given document after this document. #[inline] - pub fn append(self, that: Doc<'a, A, BoxDoc<'a, A>>) -> Doc<'a, A, BoxDoc<'a, A>> { + pub fn append(self, that: Doc<'a, BoxDoc<'a, A>, A>) -> Doc<'a, BoxDoc<'a, A>, A> { DocBuilder(&BOX_ALLOCATOR, self).append(that).into() } /// A single document concatenating all the given documents. #[inline] - pub fn concat(docs: I) -> Doc<'a, A, BoxDoc<'a, A>> + pub fn concat(docs: I) -> Doc<'a, BoxDoc<'a, A>, A> where - I: IntoIterator>>, + I: IntoIterator, A>>, { docs.into_iter().fold(Doc::nil(), |a, b| a.append(b)) } @@ -450,10 +450,10 @@ impl<'a, A> Doc<'a, A, BoxDoc<'a, A>> { /// /// Compare [the `intersperse` method from the `itertools` crate](https://docs.rs/itertools/0.5.9/itertools/trait.Itertools.html#method.intersperse). #[inline] - pub fn intersperse(docs: I, separator: S) -> Doc<'a, A, BoxDoc<'a, A>> + pub fn intersperse(docs: I, separator: S) -> Doc<'a, BoxDoc<'a, A>, A> where - I: IntoIterator>>, - S: Into>> + Clone, + I: IntoIterator, A>>, + S: Into, A>> + Clone, A: Clone, { let separator = separator.into(); @@ -476,18 +476,18 @@ impl<'a, A> Doc<'a, A, BoxDoc<'a, A>> { /// horizontally and combined into a one single line, or they are each layed out on their own /// line. #[inline] - pub fn group(self) -> Doc<'a, A, BoxDoc<'a, A>> { + pub fn group(self) -> Doc<'a, BoxDoc<'a, A>, A> { DocBuilder(&BOX_ALLOCATOR, self).group().into() } /// Increase the indentation level of this document. #[inline] - pub fn nest(self, offset: usize) -> Doc<'a, A, BoxDoc<'a, A>> { + pub fn nest(self, offset: usize) -> Doc<'a, BoxDoc<'a, A>, A> { DocBuilder(&BOX_ALLOCATOR, self).nest(offset).into() } #[inline] - pub fn annotate(self, ann: A) -> Doc<'a, A, BoxDoc<'a, A>> { + pub fn annotate(self, ann: A) -> Doc<'a, BoxDoc<'a, A>, A> { DocBuilder(&BOX_ALLOCATOR, self).annotate(ann).into() } } @@ -497,19 +497,19 @@ mod tests { use super::*; macro_rules! test { - ($size: expr, $actual: expr, $expected: expr) => { + ($size:expr, $actual:expr, $expected:expr) => { let mut s = String::new(); $actual.render_fmt($size, &mut s).unwrap(); assert_eq!(s, $expected); }; - ($actual: expr, $expected: expr) => { + ($actual:expr, $expected:expr) => { test!(70, $actual, $expected) - } + }; } #[test] fn box_doc_inference() { - let doc = Doc::<(), _>::group( + let doc = Doc::<_>::group( Doc::text("test") .append(Doc::space()) .append(Doc::text("test")), @@ -520,7 +520,7 @@ mod tests { #[test] fn forced_newline() { - let doc = Doc::<(), _>::group( + let doc = Doc::<_>::group( Doc::text("test") .append(Doc::newline()) .append(Doc::text("test")), @@ -531,7 +531,7 @@ mod tests { #[test] fn space_do_not_reset_pos() { - let doc = Doc::<(), _>::group(Doc::text("test").append(Doc::space())) + let doc = Doc::<_>::group(Doc::text("test").append(Doc::space())) .append(Doc::text("test")) .append(Doc::group(Doc::space()).append(Doc::text("test"))); @@ -542,7 +542,7 @@ mod tests { // a single line but instead breaks on the `Doc::space()` to fit with 6 columns #[test] fn newline_does_not_cause_next_line_to_be_to_long() { - let doc = Doc::<(), _>::group( + let doc = Doc::<_>::group( Doc::text("test").append(Doc::newline()).append( Doc::text("test") .append(Doc::space()) @@ -555,7 +555,7 @@ mod tests { #[test] fn block() { - let doc = Doc::<(), _>::group( + let doc = Doc::<_>::group( Doc::text("{") .append( Doc::space()