Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/alloc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ pub trait DocAllocator<'a> {
self.ascii_text(" ")
}

#[inline]
fn weak_space(&'a self) -> DocBuilder<'a, Self> {
DocBuilder(self, Doc::WeakSpace.into())
}

/// A line acts like a `\n` but behaves like `space` if it is grouped on a single line.
#[inline]
fn line(&'a self) -> DocBuilder<'a, Self> {
Expand Down Expand Up @@ -108,6 +113,16 @@ pub trait DocAllocator<'a> {
self.line_().group()
}

#[inline]
fn weak_line(&'a self) -> DocBuilder<'a, Self> {
DocBuilder(self, Doc::WeakLine.into()).flat_alt(self.space())
}

#[inline]
fn weak_line_(&'a self) -> DocBuilder<'a, Self> {
DocBuilder(self, Doc::WeakLine.into()).flat_alt(self.nil())
}

/// Equivalent to `self.nil().flat_alt(doc.pretty(self))`
#[inline]
fn if_group_flat(&'a self, doc: impl Pretty<'a, Self>) -> DocBuilder<'a, Self> {
Expand Down
16 changes: 15 additions & 1 deletion src/doc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@ where
Fail,

// Texts
WeakSpace,
HardLine,
WeakLine,
Text(Text<'a>),
TextWithLen(usize, T), // Stores the length of a string document that is not just ascii
HardLine,

// Structural
Append(T, T), // Sequencing
Expand Down Expand Up @@ -129,7 +131,9 @@ where
Doc::Nil => f.write_str("Nil"),
Doc::Fail => f.write_str("Fail"),

Doc::WeakSpace => f.write_str("WeakSpace"),
Doc::HardLine => f.write_str("HardLine"),
Doc::WeakLine => f.write_str("WeakLine"),
Doc::TextWithLen(_, d) => d.fmt(f),
Doc::Text(s) => s.fmt(f),

Expand Down Expand Up @@ -401,11 +405,21 @@ macro_rules! impl_doc_methods {
Doc::HardLine.into()
}

#[inline]
pub fn weak_line() -> Self {
Doc::WeakLine.into()
}

#[inline]
pub fn space() -> Self {
Doc::Text(Text::Borrowed(" ")).into()
}

#[inline]
pub fn weak_space() -> Self {
Doc::WeakSpace.into()
}

/// Make the parent group break
#[inline]
pub fn expand_parent() -> Self {
Expand Down
127 changes: 108 additions & 19 deletions src/render/fit.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
use crate::{visitor::visit_sequence_rev, Doc, DocPtr, Render};
use crate::{render::write::write_spaces, visitor::visit_sequence_rev, Doc, DocPtr, Render};

use super::write::{write_newline, BufferWrite};
use super::write::BufferWrite;

pub fn print_doc<'a, W, T>(doc: &Doc<'a, T>, width: usize, out: &mut W) -> Result<(), W::Error>
where
T: DocPtr<'a> + 'a,
W: ?Sized + Render,
{
Printer {
pos: 0,
cmds: vec![Cmd {
indent: 0,
mode: Mode::Break,
doc,
}],
pos: 0,
pending_indent: Some(0),
fit_docs: vec![],
line_suffixes: vec![],
width,
Expand Down Expand Up @@ -52,8 +53,9 @@ struct Printer<'d, 'a, T>
where
T: DocPtr<'a> + 'a,
{
pos: usize,
cmds: Vec<Cmd<'d, 'a, T>>,
pos: usize,
pending_indent: Option<usize>,
fit_docs: Vec<FitCmd<'d, 'a, T>>,
line_suffixes: Vec<&'d Doc<'a, T>>,
width: usize,
Expand Down Expand Up @@ -92,10 +94,14 @@ where
Doc::Nil => break,
Doc::Fail => return Err(out.fail_doc()),

Doc::WeakSpace => {
if self.pending_indent.is_none() {
fits &= self.write_str(out, " ", 1)?;
}
break;
}
Doc::Text(ref s) => {
out.write_str_all(s)?;
self.pos += s.len();
fits &= self.pos <= self.width;
fits &= self.write_str(out, s, s.len())?;
break;
}

Expand All @@ -105,9 +111,7 @@ where
Doc::Text(ref s) => s,
_ => unreachable!(),
};
out.write_str_all(str)?;
self.pos += len;
fits &= self.pos <= self.width;
fits &= self.write_str(out, str, len)?;
break;
}

Expand All @@ -122,12 +126,38 @@ where
// The next document may have different indentation so we should use it if
// we can
if let Some(next) = self.cmds.pop() {
write_newline(next.indent, out)?;
self.pos = next.indent;
self.write_newline(out)?;
// write_spaces(next.indent, out)?;
// self.pos = next.indent;
self.pending_indent = Some(next.indent);
cmd = next;
} else {
self.write_newline(out)?;
// write_spaces(indent, out)?;
// self.pos = indent;
self.pending_indent = Some(indent);
break;
}
Comment on lines 128 to +140
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here is the break. we need to work out new api for it.

}
Doc::WeakLine => {
// flush line suffixes
if self.line_suffixes.len() > ls_top {
self.cmds.push(cmd);
self.push_line_suffixes(ls_top, mode, indent);
break;
}

if self.pending_indent.is_some() {
// do not insert a newline if we are already in a fresh line
break;
}
if let Some(next) = self.cmds.pop() {
self.write_newline(out)?;
self.pending_indent = Some(next.indent);
cmd = next;
} else {
write_newline(indent, out)?;
self.pos = indent;
self.write_newline(out)?;
self.pending_indent = Some(indent);
break;
}
}
Expand All @@ -154,7 +184,7 @@ where
}
Doc::Align(ref inner) => {
// Align to the current position.
cmd.indent = self.pos;
cmd.indent = self.pending_indent.unwrap_or(0) + self.pos;
cmd.doc = inner;
}

Expand All @@ -170,7 +200,14 @@ where
};
}
Doc::Group(ref inner) => {
if mode == Mode::Break && self.fitting(inner, self.pos, indent, Mode::Flat)
if mode == Mode::Break
&& self.fitting(
inner,
self.pos,
indent,
Mode::Flat,
self.pending_indent,
)
{
cmd.mode = Mode::Flat;
}
Expand Down Expand Up @@ -208,7 +245,15 @@ where
}
}
Doc::PartialUnion(ref left, ref right) => {
if mode == Mode::Flat || self.fitting(left, self.pos, indent, Mode::Break) {
if mode == Mode::Flat
|| self.fitting(
left,
self.pos,
indent,
Mode::Break,
self.pending_indent,
)
{
cmd.doc = left;
} else {
cmd.doc = right;
Expand All @@ -235,6 +280,28 @@ where
Ok(fits)
}

fn write_str<W>(&mut self, out: &mut W, s: &str, len: usize) -> Result<bool, W::Error>
where
W: ?Sized + Render,
{
if let Some(indent) = self.pending_indent.take() {
write_spaces(indent, out)?;
self.pos = indent;
}
out.write_str_all(s)?;
self.pos += len;
Ok(self.pos <= self.width)
}

fn write_newline<W>(&mut self, out: &mut W) -> Result<(), W::Error>
where
W: ?Sized + Render,
{
out.write_str_all("\n")?;
self.pos = 0;
Ok(())
}

fn push_line_suffixes(&mut self, ls_top: usize, mode: Mode, indent: usize) {
self.line_suffixes
.drain(ls_top..)
Expand All @@ -243,7 +310,14 @@ where
}

#[cfg_attr(not(feature = "contextual"), allow(unused_variables))]
fn fitting(&mut self, next: &'d Doc<'a, T>, mut pos: usize, indent: usize, mode: Mode) -> bool {
fn fitting(
&mut self,
next: &'d Doc<'a, T>,
mut pos: usize,
indent: usize,
mode: Mode,
mut pending_indent: Option<usize>,
) -> bool {
// We start in "flat" mode and may fall back to "break" mode when backtracking.
let mut cmd_bottom = self.cmds.len();

Expand All @@ -268,22 +342,37 @@ where
Doc::Nil => break,
Doc::Fail => return false,

Doc::WeakSpace => {
if pending_indent.is_none() {
pos += 1;
if pos > self.width {
return false;
}
}
break;
}
Doc::Text(ref s) => {
if let Some(indent) = pending_indent.take() {
pos += indent;
}
pos += s.len();
if pos > self.width {
return false;
}
break;
}
Doc::TextWithLen(len, _) => {
if let Some(indent) = pending_indent.take() {
pos += indent;
}
pos += len;
if pos > self.width {
return false;
}
break;
}

Doc::HardLine => {
Doc::HardLine | Doc::WeakLine => {
// A hard_line only “fits” in break mode.
return mode == Mode::Break;
}
Expand Down
16 changes: 4 additions & 12 deletions src/render/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,22 +100,14 @@ impl Render for BufferWrite {
fn fail_doc(&self) -> Self::Error {}
}

pub(super) fn write_newline<W>(ind: usize, out: &mut W) -> Result<(), W::Error>
where
W: ?Sized + Render,
{
out.write_str_all("\n")?;
write_spaces(ind, out)
}

pub(super) fn write_spaces<W>(spaces: usize, out: &mut W) -> Result<(), W::Error>
where
W: ?Sized + Render,
{
let mut inserted = 0;
while inserted < spaces {
let insert = SPACES.len().min(spaces - inserted);
inserted += out.write_str(&SPACES[..insert])?;
let mut remaining = spaces;
while remaining > 0 {
let insert = SPACES.len().min(remaining);
remaining -= out.write_str(&SPACES[..insert])?;
}

Ok(())
Expand Down
51 changes: 51 additions & 0 deletions tests/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -352,3 +352,54 @@ fn pretty_cow() {

test_snapshot!(8, doc, @"abc 123");
}

#[test]
fn test_hard_line() {
let arena = Arena::new();
let doc = docs![&arena, "aaa", Doc::hard_line(), Doc::hard_line(), "bbb"].nest(2);

test_snapshot!(80, doc, @"aaa\n \n bbb");
}

#[test]
fn test_weak_space() {
let arena = Arena::new();
let doc = arena
.pretty((
Doc::weak_space(),
"aaa",
Doc::weak_space(),
Doc::weak_line(),
Doc::weak_space(),
"bbb",
))
.nest(2);

test_snapshot!(80, doc, @"aaa \n bbb");
}

#[test]
fn test_weak_line() {
let arena = Arena::new();
let doc = arena.pretty((
"(",
arena
.pretty((
Doc::weak_line(),
(
"aaa",
",",
Doc::weak_space(),
"// comment",
Doc::weak_line(),
),
Doc::weak_line(),
("bbb", ","),
))
.indent(2),
Doc::weak_line(),
")",
));

test_snapshot!(80, doc, @"(\n aaa, // comment\n bbb,\n)");
}
Loading