Skip to content

Commit

Permalink
Merge pull request #1 from rtfeldman/format-records
Browse files Browse the repository at this point in the history
Add formatting for records
  • Loading branch information
rtfeldman authored Nov 15, 2019
2 parents d662fe4 + 20240df commit 6c0e771
Show file tree
Hide file tree
Showing 5 changed files with 306 additions and 153 deletions.
82 changes: 82 additions & 0 deletions src/fmt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use parse::ast::{AssignedField, Expr, Pattern};

pub fn is_multiline_expr<'a>(expr: &'a Expr<'a>) -> bool {
use parse::ast::Expr::*;
// TODO cache these answers using a Map<Pointer, bool>, so
// we don't have to traverse subexpressions repeatedly

match expr {
// Return whether these spaces contain any Newlines
SpaceBefore(_, spaces) | SpaceAfter(_, spaces) => {
spaces.iter().any(|space| space.contains_newline())
}

// These expressions never have newlines
Float(_)
| Int(_)
| HexInt(_)
| OctalInt(_)
| BinaryInt(_)
| Str(_)
| Field(_, _)
| QualifiedField(_, _)
| AccessorFunction(_)
| Var(_, _)
| MalformedIdent(_)
| MalformedClosure
| Variant(_, _) => false,

// These expressions always have newlines
Defs(_, _) | Case(_, _) => true,

List(elems) => elems
.iter()
.any(|loc_expr| is_multiline_expr(&loc_expr.value)),

BlockStr(lines) => lines.len() > 1,
Apply(loc_expr, args, _) => {
is_multiline_expr(&loc_expr.value)
|| args.iter().any(|loc_arg| is_multiline_expr(&loc_arg.value))
}

If((loc_cond, loc_if_true, loc_if_false)) => {
is_multiline_expr(&loc_cond.value)
|| is_multiline_expr(&loc_if_true.value)
|| is_multiline_expr(&loc_if_false.value)
}

BinOp((loc_left, _, loc_right)) => {
is_multiline_expr(&loc_left.value) || is_multiline_expr(&loc_right.value)
}

UnaryOp(loc_subexpr, _) => is_multiline_expr(&loc_subexpr.value),

PrecedenceConflict(_, _, loc_expr) => is_multiline_expr(&loc_expr.value),

Closure(loc_patterns, loc_body) => {
// check the body first because it's more likely to be multiline
is_multiline_expr(&loc_body.value)
|| loc_patterns
.iter()
.any(|loc_pattern| is_multiline_pattern(&loc_pattern.value))
}

Record(loc_fields) => loc_fields
.iter()
.any(|loc_field| is_multiline_field(&loc_field.value)),
}
}

pub fn is_multiline_field<'a, Val>(field: &'a AssignedField<'a, Val>) -> bool {
use self::AssignedField::*;

match field {
LabeledValue(_, spaces, _) | LabelOnly(_, spaces) => !spaces.is_empty(),
AssignedField::SpaceBefore(_, _) | AssignedField::SpaceAfter(_, _) => true,
Malformed(text) => text.chars().any(|c| c == '\n'),
}
}

pub fn is_multiline_pattern<'a>(_pattern: &'a Pattern<'a>) -> bool {
panic!("TODO return iff there are any newlines")
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub mod string;

pub mod constrain;
pub mod ena;
pub mod fmt;
pub mod infer;
pub mod pretty_print_types;
pub mod solve;
Expand Down
150 changes: 140 additions & 10 deletions src/parse/ast.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use bumpalo::collections::String;
use bumpalo::collections::Vec;
use bumpalo::Bump;
use fmt;
use operator::CalledVia;
use operator::{BinOp, UnaryOp};
use parse::ident::Ident;
Expand Down Expand Up @@ -243,7 +244,17 @@ pub enum AssignedField<'a, Val> {
pub enum CommentOrNewline<'a> {
Newline,
LineComment(&'a str),
BlockComment(&'a [&'a str]),
}

impl<'a> CommentOrNewline<'a> {
pub fn contains_newline(&self) -> bool {
use self::CommentOrNewline::*;

match self {
// Line comments have an implicit newline at the end
Newline | LineComment(_) => true,
}
}
}

#[derive(Clone, Debug, PartialEq)]
Expand Down Expand Up @@ -520,7 +531,6 @@ pub fn format<'a>(
}
SpaceAfter(sub_expr, spaces) => {
buf.push_str(&format(arena, sub_expr, indent, apply_needs_parens));

buf.push_str(&format_spaces(arena, spaces.iter(), indent));
}
Str(string) => {
Expand Down Expand Up @@ -580,8 +590,43 @@ pub fn format<'a>(
Record(loc_fields) => {
buf.push('{');

for _field in loc_fields {
panic!("TODO implement Display for record fields.");
let is_multiline = loc_fields
.iter()
.any(|loc_field| fmt::is_multiline_field(&loc_field.value));

let mut iter = loc_fields.iter().peekable();
let field_indent = if is_multiline {
indent + 4
} else {
if !loc_fields.is_empty() {
buf.push(' ');
}

indent
};

while let Some(field) = iter.next() {
buf.push_str(&format_field(
arena,
&field.value,
is_multiline,
field_indent,
apply_needs_parens,
));

if let Some(_) = iter.peek() {
buf.push(',');

if !is_multiline {
buf.push(' ');
}
}
}

if is_multiline {
buf.push('\n');
} else if !loc_fields.is_empty() {
buf.push(' ');
}

buf.push('}');
Expand Down Expand Up @@ -785,18 +830,103 @@ where
// Reset to 1 because we just printed a \n
consecutive_newlines = 1;
}
BlockComment(lines) => {
buf.push_str("###");
}
}

buf
}

/// Like format_spaces, but remove newlines and keep only comments.
fn format_comments_only<'a, I>(arena: &'a Bump, spaces: I, _indent: u16) -> String<'a>
where
I: Iterator<Item = &'a CommentOrNewline<'a>>,
{
use self::CommentOrNewline::*;

for line in lines.iter() {
buf.push_str(line);
let mut buf = String::new_in(arena);

for space in spaces {
match space {
Newline => {}
LineComment(comment) => {
buf.push('#');
buf.push_str(comment);
buf.push('\n');
}
}
}

buf
}

pub fn format_field<'a>(
arena: &'a Bump,
assigned_field: &'a AssignedField<'a, Expr<'a>>,
is_multiline: bool,
indent: u16,
apply_needs_parens: bool,
) -> String<'a> {
use self::AssignedField::*;

let mut buf = String::new_in(arena);

match assigned_field {
LabeledValue(name, spaces, value) => {
if is_multiline {
buf.push('\n');

for _ in 0..indent {
buf.push(' ');
}
}

buf.push_str("###");
buf.push_str(name.value);

consecutive_newlines = 0;
if !spaces.is_empty() {
buf.push_str(&format_spaces(arena, spaces.iter(), indent));
}

buf.push(':');
buf.push(' ');
buf.push_str(&format(arena, &value.value, indent, apply_needs_parens));
}
LabelOnly(name, spaces) => {
if is_multiline {
buf.push('\n');

for _ in 0..indent {
buf.push(' ');
}
}

buf.push_str(name.value);

if !spaces.is_empty() {
buf.push(' ');
buf.push_str(&format_spaces(arena, spaces.iter(), indent));
}
}
AssignedField::SpaceBefore(sub_expr, spaces) => {
buf.push_str(&format_comments_only(arena, spaces.iter(), indent));
buf.push_str(&format_field(
arena,
sub_expr,
is_multiline,
indent,
apply_needs_parens,
));
}
AssignedField::SpaceAfter(sub_expr, spaces) => {
buf.push_str(&format_field(
arena,
sub_expr,
is_multiline,
indent,
apply_needs_parens,
));
buf.push_str(&format_comments_only(arena, spaces.iter(), indent));
}
Malformed(string) => buf.push_str(string),
}

buf
Expand Down
Loading

0 comments on commit 6c0e771

Please sign in to comment.