Skip to content

Commit

Permalink
fix: Preserve newlines between record fields properly
Browse files Browse the repository at this point in the history
  • Loading branch information
Marwes committed Jul 4, 2017
1 parent f9836f9 commit 8f169ad
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 39 deletions.
86 changes: 51 additions & 35 deletions base/src/pretty_print.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ use std::iter::{once, repeat};
use itertools::{Either, Itertools};
use pretty::{Arena, DocAllocator, DocBuilder};

use ast::{Expr, Comment, CommentType, is_operator_char, Pattern, SpannedExpr,
SpannedPattern, ValueBinding};
use ast::{Expr, Comment, CommentType, is_operator_char, Pattern, SpannedExpr, SpannedPattern,
ValueBinding};
use kind::Kind;
use pos::{BytePos, Line, Span};
use pos::{BytePos, Span};
use source::Source;
use types::{Prec, Type, pretty_print as pretty_type};

Expand Down Expand Up @@ -164,7 +164,13 @@ where

macro_rules! newlines_iter {
($self_: ident, $iterable: expr) => {
$iterable.into_iter().tuple_windows().map(|(prev, next)| $self_.newlines(prev.end, next.start))
$iterable.into_iter().tuple_windows().map(|(prev, next)| $self_.comments(Span::new(prev.end, next.start)))
}
}

macro_rules! rev_newlines_iter {
($self_: ident, $iterable: expr) => {
$iterable.into_iter().tuple_windows().map(|(prev, next)| $self_.rev_comments(Span::new(prev.end, next.start)))
}
}

Expand Down Expand Up @@ -194,45 +200,48 @@ impl<'a> ExprPrinter<'a> {
.collect()
}


fn newlines(&'a self, prev: BytePos, next: BytePos) -> DocBuilder<'a, Arena<'a>> {
fn comments(&'a self, span: Span<BytePos>) -> DocBuilder<'a, Arena<'a>> {
let arena = &self.arena;
let prev_line = self.source.line_number_at_byte(prev);
let next_line = self.source.line_number_at_byte(next);
// Record expressions
let empty_lines = (prev_line.to_usize()..next_line.to_usize())
.map(|l| {
self.source.line(Line::from(l)).unwrap().1.trim().is_empty()
})
.count();
arena.concat(repeat(arena.newline()).take(empty_lines))
arena.concat(self.source.comments_between(span).map(|comment| {
chain![arena;
if comment.is_empty() {
arena.nil()
} else {
arena.text("// ").append(comment)
},
arena.newline()
]
}))
}

fn comments(&'a self, span: Span<BytePos>) -> DocBuilder<'a, Arena<'a>> {
fn rev_comments(&'a self, span: Span<BytePos>) -> DocBuilder<'a, Arena<'a>> {
let arena = &self.arena;
arena
.concat(
self.source
.comments_between(span)
.map(|comment| {
chain![arena;
self.source
.comments_between(span)
.rev()
.map(|comment| {
chain![arena;
if comment.is_empty() {
arena.nil()
} else {
arena.text("// ").append(comment)
},
arena.newline()
]
}),
)
})
.fold(arena.nil(), |acc, doc| doc.append(acc))
}

pub fn pretty_expr<Id>(&'a self, expr: &'a SpannedExpr<Id>) -> DocBuilder<'a, Arena<'a>>
where
Id: AsRef<str>,
{
self.pretty_expr_(BytePos::from(0), expr)
.append(self.comments(Span::new(expr.span.end, BytePos::from(self.source.src().len()))))
self.pretty_expr_(BytePos::from(0), expr).append(
self.comments(Span::new(
expr.span.end,
BytePos::from(self.source.src().len()),
)),
)
}

pub fn pretty_expr_<Id>(
Expand Down Expand Up @@ -479,9 +488,7 @@ impl<'a> ExprPrinter<'a> {
},
)
};

let newlines = newlines_iter!(
self,
let spans = || {
ordered_iter().map(|x| {
x.either(|l| l.name.span, |r| {
Span::new(
Expand All @@ -492,18 +499,27 @@ impl<'a> ExprPrinter<'a> {
)
})
})
).collect::<Vec<_>>();
};

// Preserve comments before and after the comma
let newlines = newlines_iter!(self, spans())
.zip(rev_newlines_iter!(self, spans()))
.collect::<Vec<_>>();

let mut line = newline(arena, expr);
// If there are any explicit line breaks then we need put each field on a separate line
if newlines.iter().any(|doc| doc.1 != arena.nil().1) {
if newlines.iter().any(|&(ref l, ref r)| l.1 != arena.nil().1 || r.1 != arena.nil().1) {
line = arena.newline();
}
let end_iter = newlines.into_iter().map(|mut doc| {
if doc.1 == arena.nil().1 {
doc = line.clone();
let end_iter = newlines.into_iter().map(|(l_doc, mut r_doc)| {
if r_doc.1 == arena.nil().1 {
r_doc = line.clone();
}
arena.text(",").append(doc)
chain![arena;
l_doc,
",",
r_doc
]
});

let record = chain![arena;
Expand Down
14 changes: 10 additions & 4 deletions base/src/source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,13 +168,19 @@ impl<'a> DoubleEndedIterator for CommentIter<'a> {
} else {
self.src = self.src
.trim_right_matches(|c: char| c.is_whitespace() && c != '\n');
if self.src.starts_with("\n") {
if self.src.ends_with("\n") {
let comment_line = self.src[..self.src.len() - 1].lines().next_back().unwrap();
// Add 1 to skip `\n' as well
self.src = &self.src[..(self.src.len() - 2 - comment_line.len() - 1)];

let trimmed = comment_line.trim_left();

let newline_len = if self.src.ends_with("\r\n") {
2
} else {
1
};
self.src = &self.src[..(self.src.len() - newline_len)];

if trimmed.starts_with("//") && !trimmed.starts_with("///") {
self.src = &self.src[..(self.src.len() - 2 - comment_line.len() - 1)];
Some(trimmed)
} else {
Some("")
Expand Down
13 changes: 13 additions & 0 deletions parser/tests/pretty_print.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,16 @@ type Test = Int
"#;
assert_eq!(&format_expr(expr).unwrap(), expr);
}

#[test]
fn preserve_whitespace_in_record() {
let expr = r#"
{
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaax = 1,
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbby = 2
}
"#;
assert_eq!(&format_expr(expr).unwrap(), expr);
}

0 comments on commit 8f169ad

Please sign in to comment.