Skip to content

Commit

Permalink
feat: Improve readability of large types by splitting them onto multi…
Browse files Browse the repository at this point in the history
…ple lines

This uses the [pretty](https://github.com/freebroccolo/pretty.rs) library to automatically split lines which are longer that some threshold (80 currently) at reasonable points.
  • Loading branch information
Marwes committed Aug 16, 2016
1 parent bc664cb commit 1c296ac
Show file tree
Hide file tree
Showing 4 changed files with 207 additions and 101 deletions.
1 change: 1 addition & 0 deletions base/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ documentation = "https://marwes.github.io/gluon/gluon/index.html"
log = "0.3.6"
quick-error = "1.0.0"
fnv = "1.0.3"
pretty = "0.1.0"
1 change: 1 addition & 0 deletions base/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
extern crate log;
#[macro_use]
extern crate quick_error;
extern crate pretty;

pub mod ast;
pub mod fixed;
Expand Down
180 changes: 114 additions & 66 deletions base/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -674,112 +674,160 @@ pub struct DisplayType<'a, I: 'a, T: 'a, E: 'a> {
env: &'a E,
}

use pretty::{DocAllocator, Arena, DocBuilder};

impl<'a, I, T, E> fmt::Display for DisplayType<'a, I, T, E>
where E: DisplayEnv<Ident = I> + 'a,
T: Deref<Target = Type<I, T>> + 'a
T: Deref<Target = Type<I, T>> + 'a,
I: AsRef<str>
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// Standard width for terminals are 80 characters
const WIDTH: usize = 80;

let arena = Arena::new();
let mut s = Vec::new();
try!(self.pretty(&arena)
.group()
.1
.render(WIDTH, &mut s)
.map_err(|_| fmt::Error));
s.pop();// Remove the ending newline
write!(f, "{}", ::std::str::from_utf8(&s).expect("utf-8"))
}
}

fn enclose<'a>(p: Prec,
limit: Prec,
arena: &'a Arena<'a>,
doc: DocBuilder<'a, Arena<'a>>)
-> DocBuilder<'a, Arena<'a>> {
let pre = if p >= limit {
arena.text("(")
} else {
arena.nil()
};
let mid = pre.append(doc);
if p >= limit {
mid.append(arena.text(")"))
} else {
mid
}
}

impl<'a, I, T, E> DisplayType<'a, I, T, E>
where E: DisplayEnv<Ident = I> + 'a,
T: Deref<Target = Type<I, T>> + 'a
{
fn pretty(&self, arena: &'a Arena<'a>) -> DocBuilder<'a, Arena<'a>>
where I: AsRef<str>
{
let p = self.prec;
match *self.typ {
Type::Variable(ref var) => write!(f, "{}", var),
Type::Generic(ref gen) => write!(f, "{}", self.env.string(&gen.id)),
Type::Variable(ref var) => arena.text(format!("{}", var.id)),
Type::Generic(ref gen) => arena.text(gen.id.as_ref()),
Type::App(ref t, ref args) => {
match self.typ.as_function() {
Some((arg, ret)) => {
if p >= Prec::Function {
write!(f, "({} -> {})", top(self.env, arg), top(self.env, ret))
} else {
write!(f,
"{} -> {}",
dt(self.env, Prec::Function, arg),
top(self.env, ret))
}
let doc = dt(self.env, Prec::Function, arg)
.pretty(arena)
.group()
.append(" ->")
.append(arena.newline())
.append(top(self.env, ret).pretty(arena));

enclose(p, Prec::Function, arena, doc)
}
None => {
if p >= Prec::Constructor {
try!(write!(f, "("));
}
try!(write!(f, "{}", dt(self.env, Prec::Top, t)));
let mut doc = dt(self.env, Prec::Top, t).pretty(arena);
for arg in args {
try!(write!(f, " {}", dt(self.env, Prec::Constructor, arg)));
doc = doc.append(arena.newline())
.append(dt(self.env, Prec::Constructor, arg).pretty(arena));
}
if p >= Prec::Constructor {
try!(write!(f, ")"));
}
Ok(())
enclose(p, Prec::Constructor, arena, doc).group()
}
}
}
Type::Variants(ref variants) => {
if p >= Prec::Constructor {
try!(write!(f, "("));
}
let mut first = true;
let mut doc = arena.nil();
for variant in variants {
if !first {
try!(write!(f, " "));
doc = doc.append(arena.newline());
}
first = false;
try!(write!(f, "| {}", self.env.string(&variant.0)));
doc = doc.append("| ")
.append(variant.0.as_ref());
for arg in arg_iter(&variant.1) {
try!(write!(f, " {}", dt(self.env, Prec::Constructor, &arg)));
doc = doc.append(" ")
.append(dt(self.env, Prec::Constructor, &arg).pretty(arena))
}
}
if p >= Prec::Constructor {
try!(write!(f, ")"));
}
Ok(())
enclose(p, Prec::Constructor, arena, doc).group()
}
Type::Builtin(ref t) => t.fmt(f),
Type::Builtin(ref t) => arena.text(t.to_str()),
Type::Record { ref types, ref fields } => {
try!(write!(f, "{{"));
let mut doc = arena.text("{");
if !types.is_empty() {
try!(write!(f, " {} ", self.env.string(&types[0].name)));
for arg in &types[0].typ.args {
try!(write!(f, "{} ", self.env.string(&arg.id)));
}
match types[0].typ.typ {
Some(ref typ) => try!(write!(f, "= {}", top(self.env, typ))),
None => try!(write!(f, "= <abstract>")),
}
for field in &types[1..] {
try!(write!(f, ", {} ", self.env.string(&field.name)));
for (i, field) in types.iter().enumerate() {
let comma = if i + 1 != types.len() || !fields.is_empty() {
arena.text(",")
} else {
arena.nil()
};
let mut f = arena.text(self.env.string(&field.name))
.append(" ");
for arg in &field.typ.args {
try!(write!(f, "{} ", self.env.string(&arg.id)));
}
match field.typ.typ {
Some(ref typ) => try!(write!(f, "= {}", top(self.env, typ))),
None => try!(write!(f, "= <abstract>")),
f = f.append(self.env.string(&arg.id))
.append(" ");
}
}
if fields.is_empty() {
try!(write!(f, " "));
f = match field.typ.typ {
Some(ref typ) => {
f.append("= ")
.append(top(self.env, typ).pretty(arena))
}
None => f.append("= <abstract>"),
};
f = f.append(comma)
.group();
doc = doc.append(arena.newline()).append(f);
}
}
if !fields.is_empty() {
if !types.is_empty() {
try!(write!(f, ","));
}
try!(write!(f,
" {}: {}",
self.env.string(&fields[0].name),
top(self.env, &*fields[0].typ)));
for field in &fields[1..] {
try!(write!(f,
", {}: {}",
self.env.string(&field.name),
top(self.env, &*field.typ)));
for (i, field) in fields.iter().enumerate() {
let comma = if i + 1 != fields.len() {
arena.text(",")
} else {
arena.nil()
};
let mut rhs = top(self.env, &*field.typ).pretty(arena);
match *field.typ {
// Records handle nesting on their own
Type::Record { .. } => (),
_ => rhs = rhs.nest(4),
}
let f = arena.text(self.env.string(&field.name))
.append(": ")
.append(rhs.group())
.append(comma)
.group();
doc = doc.append(arena.newline()).append(f);
}
try!(write!(f, " "));
}
write!(f, "}}")
doc = doc.nest(4);
if !types.is_empty() || !fields.is_empty() {
doc = doc.append(arena.newline());
}
doc.append("}")
.group()
}
Type::Id(ref id) => write!(f, "{}", self.env.string(id)),
Type::Alias(ref alias) => write!(f, "{}", self.env.string(&alias.name)),
Type::Id(ref id) => arena.text(self.env.string(id)),
Type::Alias(ref alias) => arena.text(self.env.string(&alias.name)),
}
}
}


#[derive(PartialEq, Copy, Clone, PartialOrd)]
enum Prec {
/// The type exists in the top context, no parentheses needed.
Expand Down
Loading

0 comments on commit 1c296ac

Please sign in to comment.