Improve rendering of type-signatures in docs
- [x] Display function arguments using a newline-multiline strategy when the signature gets too long. For example: ``` union_with ( left left: AssocList<key, value> , right right: AssocList<key, value> , with with: fn(key, value, value) -> value ) -> AssocList<key, value> ``` - [x] Show type-aliases as type-aliases in signatures; provided they've been specified as type annotations. Otherwise, fallback to the inferred type. - [x] Do not show argument names in signatures, but show labels when they're present. This reflects more the original intent behind labels (which are meant as public-facing documentation).
This commit is contained in:
parent
579030db36
commit
202678e21e
|
@ -18,6 +18,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
const INDENT: isize = 2;
|
const INDENT: isize = 2;
|
||||||
|
const DOCS_MAX_COLUMNS: isize = 80;
|
||||||
|
|
||||||
pub fn pretty(writer: &mut String, module: UntypedModule, extra: ModuleExtra, src: &str) {
|
pub fn pretty(writer: &mut String, module: UntypedModule, extra: ModuleExtra, src: &str) {
|
||||||
let intermediate = Intermediate {
|
let intermediate = Intermediate {
|
||||||
|
@ -347,6 +348,7 @@ impl<'comments> Formatter<'comments> {
|
||||||
} => name
|
} => name
|
||||||
.to_doc()
|
.to_doc()
|
||||||
.append(wrap_args(
|
.append(wrap_args(
|
||||||
|
false,
|
||||||
args.iter()
|
args.iter()
|
||||||
.map(|a| (self.constant_call_arg(a), a.label.is_some())),
|
.map(|a| (self.constant_call_arg(a), a.label.is_some())),
|
||||||
))
|
))
|
||||||
|
@ -362,6 +364,7 @@ impl<'comments> Formatter<'comments> {
|
||||||
.append(".")
|
.append(".")
|
||||||
.append(name.as_str())
|
.append(name.as_str())
|
||||||
.append(wrap_args(
|
.append(wrap_args(
|
||||||
|
false,
|
||||||
args.iter()
|
args.iter()
|
||||||
.map(|a| (self.constant_call_arg(a), a.label.is_some())),
|
.map(|a| (self.constant_call_arg(a), a.label.is_some())),
|
||||||
))
|
))
|
||||||
|
@ -380,6 +383,7 @@ impl<'comments> Formatter<'comments> {
|
||||||
Constant::Tuple { elements, .. } => "#"
|
Constant::Tuple { elements, .. } => "#"
|
||||||
.to_doc()
|
.to_doc()
|
||||||
.append(wrap_args(
|
.append(wrap_args(
|
||||||
|
false,
|
||||||
elements.iter().map(|e| (self.const_expr(e), false)),
|
elements.iter().map(|e| (self.const_expr(e), false)),
|
||||||
))
|
))
|
||||||
.group(),
|
.group(),
|
||||||
|
@ -451,15 +455,19 @@ impl<'comments> Formatter<'comments> {
|
||||||
..
|
..
|
||||||
} => "fn"
|
} => "fn"
|
||||||
.to_doc()
|
.to_doc()
|
||||||
.append(wrap_args(args.iter().map(|t| (self.annotation(t), false))))
|
.append(wrap_args(
|
||||||
|
false,
|
||||||
|
args.iter().map(|t| (self.annotation(t), false)),
|
||||||
|
))
|
||||||
.group()
|
.group()
|
||||||
.append(" ->")
|
.append(" ->")
|
||||||
.append(break_("", " ").append(self.annotation(retrn)).nest(INDENT)),
|
.append(break_("", " ").append(self.annotation(retrn)).nest(INDENT)),
|
||||||
|
|
||||||
Annotation::Var { name, .. } => name.to_doc(),
|
Annotation::Var { name, .. } => name.to_doc(),
|
||||||
Annotation::Tuple { elems, .. } => "#"
|
Annotation::Tuple { elems, .. } => "#".to_doc().append(wrap_args(
|
||||||
.to_doc()
|
false,
|
||||||
.append(wrap_args(elems.iter().map(|t| (self.annotation(t), false)))),
|
elems.iter().map(|t| (self.annotation(t), false)),
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
.group()
|
.group()
|
||||||
}
|
}
|
||||||
|
@ -519,7 +527,10 @@ impl<'comments> Formatter<'comments> {
|
||||||
.append(keyword)
|
.append(keyword)
|
||||||
.append(" ")
|
.append(" ")
|
||||||
.append(name)
|
.append(name)
|
||||||
.append(wrap_args(args.iter().map(|e| (self.fn_arg(e), false))));
|
.append(wrap_args(
|
||||||
|
false,
|
||||||
|
args.iter().map(|e| (self.fn_arg(e), false)),
|
||||||
|
));
|
||||||
|
|
||||||
// Add return annotation
|
// Add return annotation
|
||||||
let head = match return_annotation {
|
let head = match return_annotation {
|
||||||
|
@ -550,7 +561,7 @@ impl<'comments> Formatter<'comments> {
|
||||||
return_annotation: Option<&'a Annotation>,
|
return_annotation: Option<&'a Annotation>,
|
||||||
body: &'a UntypedExpr,
|
body: &'a UntypedExpr,
|
||||||
) -> Document<'a> {
|
) -> Document<'a> {
|
||||||
let args = wrap_args(args.iter().map(|e| (self.fn_arg(e), false))).group();
|
let args = wrap_args(false, args.iter().map(|e| (self.fn_arg(e), false))).group();
|
||||||
let body = match body {
|
let body = match body {
|
||||||
UntypedExpr::When { .. } => self.expr(body).force_break(),
|
UntypedExpr::When { .. } => self.expr(body).force_break(),
|
||||||
_ => self.expr(body),
|
_ => self.expr(body),
|
||||||
|
@ -780,7 +791,10 @@ impl<'comments> Formatter<'comments> {
|
||||||
|
|
||||||
UntypedExpr::Tuple { elems, .. } => "#"
|
UntypedExpr::Tuple { elems, .. } => "#"
|
||||||
.to_doc()
|
.to_doc()
|
||||||
.append(wrap_args(elems.iter().map(|e| (self.wrap_expr(e), false))))
|
.append(wrap_args(
|
||||||
|
false,
|
||||||
|
elems.iter().map(|e| (self.wrap_expr(e), false)),
|
||||||
|
))
|
||||||
.group(),
|
.group(),
|
||||||
};
|
};
|
||||||
commented(document, comments)
|
commented(document, comments)
|
||||||
|
@ -844,6 +858,7 @@ impl<'comments> Formatter<'comments> {
|
||||||
|
|
||||||
_ => name
|
_ => name
|
||||||
.append(wrap_args(
|
.append(wrap_args(
|
||||||
|
false,
|
||||||
args.iter().map(|a| (self.pattern_call_arg(a), is_record)),
|
args.iter().map(|a| (self.pattern_call_arg(a), is_record)),
|
||||||
))
|
))
|
||||||
.group(),
|
.group(),
|
||||||
|
@ -885,6 +900,7 @@ impl<'comments> Formatter<'comments> {
|
||||||
_ => self
|
_ => self
|
||||||
.expr(fun)
|
.expr(fun)
|
||||||
.append(wrap_args(
|
.append(wrap_args(
|
||||||
|
false,
|
||||||
args.iter()
|
args.iter()
|
||||||
.map(|a| (self.call_arg(a, needs_curly), needs_curly)),
|
.map(|a| (self.call_arg(a, needs_curly), needs_curly)),
|
||||||
))
|
))
|
||||||
|
@ -932,7 +948,9 @@ impl<'comments> Formatter<'comments> {
|
||||||
let spread_doc = "..".to_doc().append(self.expr(&spread.base));
|
let spread_doc = "..".to_doc().append(self.expr(&spread.base));
|
||||||
let arg_docs = args.iter().map(|a| (self.record_update_arg(a), true));
|
let arg_docs = args.iter().map(|a| (self.record_update_arg(a), true));
|
||||||
let all_arg_docs = once((spread_doc, true)).chain(arg_docs);
|
let all_arg_docs = once((spread_doc, true)).chain(arg_docs);
|
||||||
constructor_doc.append(wrap_args(all_arg_docs)).group()
|
constructor_doc
|
||||||
|
.append(wrap_args(false, all_arg_docs))
|
||||||
|
.group()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bin_op<'a>(
|
pub fn bin_op<'a>(
|
||||||
|
@ -1018,6 +1036,7 @@ impl<'comments> Formatter<'comments> {
|
||||||
// x |> fun(_, 2, 3)
|
// x |> fun(_, 2, 3)
|
||||||
self.expr(fun).append(
|
self.expr(fun).append(
|
||||||
wrap_args(
|
wrap_args(
|
||||||
|
false,
|
||||||
args.iter()
|
args.iter()
|
||||||
.skip(1)
|
.skip(1)
|
||||||
.map(|a| (self.call_arg(a, false), false)),
|
.map(|a| (self.call_arg(a, false), false)),
|
||||||
|
@ -1026,8 +1045,9 @@ impl<'comments> Formatter<'comments> {
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
// x |> fun(1, _, 3)
|
// x |> fun(1, _, 3)
|
||||||
self.expr(fun)
|
self.expr(fun).append(
|
||||||
.append(wrap_args(args.iter().map(|a| (self.call_arg(a, false), false))).group())
|
wrap_args(false, args.iter().map(|a| (self.call_arg(a, false), false))).group(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1047,7 +1067,7 @@ impl<'comments> Formatter<'comments> {
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => self.expr(fun).append(
|
_ => self.expr(fun).append(
|
||||||
wrap_args(args.iter().map(|a| (self.call_arg(a, false), false))).group(),
|
wrap_args(false, args.iter().map(|a| (self.call_arg(a, false), false))).group(),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1091,7 +1111,9 @@ impl<'comments> Formatter<'comments> {
|
||||||
constructor
|
constructor
|
||||||
.name
|
.name
|
||||||
.to_doc()
|
.to_doc()
|
||||||
.append(wrap_args(constructor.arguments.iter().map(
|
.append(wrap_args(
|
||||||
|
false,
|
||||||
|
constructor.arguments.iter().map(
|
||||||
|RecordConstructorArg {
|
|RecordConstructorArg {
|
||||||
label,
|
label,
|
||||||
annotation,
|
annotation,
|
||||||
|
@ -1101,7 +1123,9 @@ impl<'comments> Formatter<'comments> {
|
||||||
let arg_comments = self.pop_comments(location.start);
|
let arg_comments = self.pop_comments(location.start);
|
||||||
|
|
||||||
let arg = match label {
|
let arg = match label {
|
||||||
Some(l) => l.to_doc().append(": ").append(self.annotation(annotation)),
|
Some(l) => {
|
||||||
|
l.to_doc().append(": ").append(self.annotation(annotation))
|
||||||
|
}
|
||||||
None => self.annotation(annotation),
|
None => self.annotation(annotation),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1113,7 +1137,8 @@ impl<'comments> Formatter<'comments> {
|
||||||
label.is_some(),
|
label.is_some(),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
)))
|
),
|
||||||
|
))
|
||||||
.group()
|
.group()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1247,30 +1272,72 @@ impl<'comments> Formatter<'comments> {
|
||||||
&mut self,
|
&mut self,
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
args: &'a [TypedArg],
|
args: &'a [TypedArg],
|
||||||
|
return_annotation: &'a Option<Annotation>,
|
||||||
return_type: Arc<Type>,
|
return_type: Arc<Type>,
|
||||||
) -> Document<'a> {
|
) -> Document<'a> {
|
||||||
let mut printer = tipo::pretty::Printer::new();
|
let head = name
|
||||||
name.to_doc()
|
.to_doc()
|
||||||
.append(self.docs_fn_args(args, &mut printer))
|
.append(self.docs_fn_args(false, args))
|
||||||
.append(" -> ".to_doc())
|
.append(" -> ");
|
||||||
.append(printer.print(&return_type))
|
|
||||||
|
let tail = self.type_or_annotation(return_annotation, &return_type);
|
||||||
|
|
||||||
|
let doc = head.append(tail.clone()).group();
|
||||||
|
|
||||||
|
// Wrap arguments on multi-lines if they are lengthy.
|
||||||
|
if doc
|
||||||
|
.clone()
|
||||||
|
.to_pretty_string(DOCS_MAX_COLUMNS)
|
||||||
|
.contains('\n')
|
||||||
|
{
|
||||||
|
let head = name
|
||||||
|
.to_doc()
|
||||||
|
.append(self.docs_fn_args(true, args))
|
||||||
|
.append(" -> ");
|
||||||
|
head.append(tail).group()
|
||||||
|
} else {
|
||||||
|
doc
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Will always print the types, even if they were implicit in the original source
|
// Will always print the types, even if they were implicit in the original source
|
||||||
pub fn docs_fn_args<'a>(
|
pub fn docs_fn_args<'a>(&mut self, multiline: bool, args: &'a [TypedArg]) -> Document<'a> {
|
||||||
|
if multiline {
|
||||||
|
line().nest(INDENT).append(wrap_args(
|
||||||
|
true,
|
||||||
|
args.iter()
|
||||||
|
.map(|e| (self.docs_fn_arg(e).append(line()), false)),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
wrap_args(false, args.iter().map(|e| (self.docs_fn_arg(e), false)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn docs_fn_arg<'a>(&mut self, arg: &'a Arg<Arc<Type>>) -> Document<'a> {
|
||||||
|
self.docs_fn_arg_name(&arg.arg_name)
|
||||||
|
.append(self.type_or_annotation(&arg.annotation, &arg.tipo))
|
||||||
|
.group()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn docs_fn_arg_name<'a>(&mut self, arg_name: &'a ArgName) -> Document<'a> {
|
||||||
|
match arg_name {
|
||||||
|
ArgName::Named { .. } | ArgName::Discard { .. } => "".to_doc(),
|
||||||
|
ArgName::LabeledDiscard { label, .. } | ArgName::NamedLabeled { label, .. } => {
|
||||||
|
label.to_doc().append(": ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display type-annotation when available, or fallback to inferred type.
|
||||||
|
fn type_or_annotation<'a>(
|
||||||
&mut self,
|
&mut self,
|
||||||
args: &'a [TypedArg],
|
annotation: &'a Option<Annotation>,
|
||||||
printer: &mut tipo::pretty::Printer,
|
type_info: &Arc<Type>,
|
||||||
) -> Document<'a> {
|
) -> Document<'a> {
|
||||||
wrap_args(args.iter().map(|arg| {
|
match annotation {
|
||||||
(
|
Some(a) => self.annotation(a),
|
||||||
arg.arg_name
|
None => tipo::pretty::Printer::new().print(type_info),
|
||||||
.to_doc()
|
}
|
||||||
.append(": ".to_doc().append(printer.print(&arg.tipo)))
|
|
||||||
.group(),
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wrap_expr<'a>(&mut self, expr: &'a UntypedExpr) -> Document<'a> {
|
fn wrap_expr<'a>(&mut self, expr: &'a UntypedExpr) -> Document<'a> {
|
||||||
|
@ -1403,7 +1470,10 @@ impl<'comments> Formatter<'comments> {
|
||||||
|
|
||||||
Pattern::Tuple { elems, .. } => "#"
|
Pattern::Tuple { elems, .. } => "#"
|
||||||
.to_doc()
|
.to_doc()
|
||||||
.append(wrap_args(elems.iter().map(|e| (self.pattern(e), false))))
|
.append(wrap_args(
|
||||||
|
false,
|
||||||
|
elems.iter().map(|e| (self.pattern(e), false)),
|
||||||
|
))
|
||||||
.group(),
|
.group(),
|
||||||
|
|
||||||
Pattern::List { elements, tail, .. } => {
|
Pattern::List { elements, tail, .. } => {
|
||||||
|
@ -1559,7 +1629,7 @@ impl<'a> Documentable<'a> for &'a BinOp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wrap_args<'a, I>(args: I) -> Document<'a>
|
pub fn wrap_args<'a, I>(multiline: bool, args: I) -> Document<'a>
|
||||||
where
|
where
|
||||||
I: IntoIterator<Item = (Document<'a>, bool)>,
|
I: IntoIterator<Item = (Document<'a>, bool)>,
|
||||||
{
|
{
|
||||||
|
@ -1575,6 +1645,8 @@ where
|
||||||
|
|
||||||
let (open_broken, open_unbroken, close) = if curly {
|
let (open_broken, open_unbroken, close) = if curly {
|
||||||
(" {", " { ", "}")
|
(" {", " { ", "}")
|
||||||
|
} else if multiline {
|
||||||
|
("( ", "( ", ")")
|
||||||
} else {
|
} else {
|
||||||
("(", "(", ")")
|
("(", "(", ")")
|
||||||
};
|
};
|
||||||
|
|
|
@ -410,6 +410,7 @@ impl DocFunction {
|
||||||
.docs_fn_signature(
|
.docs_fn_signature(
|
||||||
&func_def.name,
|
&func_def.name,
|
||||||
&func_def.arguments,
|
&func_def.arguments,
|
||||||
|
&func_def.return_annotation,
|
||||||
func_def.return_type.clone(),
|
func_def.return_type.clone(),
|
||||||
)
|
)
|
||||||
.to_pretty_string(MAX_COLUMNS),
|
.to_pretty_string(MAX_COLUMNS),
|
||||||
|
|
Loading…
Reference in New Issue