From 202678e21eeb3758ed37a3db093fb749ee6a7744 Mon Sep 17 00:00:00 2001 From: KtorZ Date: Sat, 17 Dec 2022 12:45:48 +0100 Subject: [PATCH] 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 , right right: AssocList , with with: fn(key, value, value) -> value ) -> AssocList ``` - [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). --- crates/lang/src/format.rs | 174 ++++++++++++++++++++++++++----------- crates/project/src/docs.rs | 1 + 2 files changed, 124 insertions(+), 51 deletions(-) diff --git a/crates/lang/src/format.rs b/crates/lang/src/format.rs index f91592d7..da68f7d9 100644 --- a/crates/lang/src/format.rs +++ b/crates/lang/src/format.rs @@ -18,6 +18,7 @@ use crate::{ }; const INDENT: isize = 2; +const DOCS_MAX_COLUMNS: isize = 80; pub fn pretty(writer: &mut String, module: UntypedModule, extra: ModuleExtra, src: &str) { let intermediate = Intermediate { @@ -347,6 +348,7 @@ impl<'comments> Formatter<'comments> { } => name .to_doc() .append(wrap_args( + false, args.iter() .map(|a| (self.constant_call_arg(a), a.label.is_some())), )) @@ -362,6 +364,7 @@ impl<'comments> Formatter<'comments> { .append(".") .append(name.as_str()) .append(wrap_args( + false, args.iter() .map(|a| (self.constant_call_arg(a), a.label.is_some())), )) @@ -380,6 +383,7 @@ impl<'comments> Formatter<'comments> { Constant::Tuple { elements, .. } => "#" .to_doc() .append(wrap_args( + false, elements.iter().map(|e| (self.const_expr(e), false)), )) .group(), @@ -451,15 +455,19 @@ impl<'comments> Formatter<'comments> { .. } => "fn" .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() .append(" ->") .append(break_("", " ").append(self.annotation(retrn)).nest(INDENT)), Annotation::Var { name, .. } => name.to_doc(), - Annotation::Tuple { elems, .. } => "#" - .to_doc() - .append(wrap_args(elems.iter().map(|t| (self.annotation(t), false)))), + Annotation::Tuple { elems, .. } => "#".to_doc().append(wrap_args( + false, + elems.iter().map(|t| (self.annotation(t), false)), + )), } .group() } @@ -519,7 +527,10 @@ impl<'comments> Formatter<'comments> { .append(keyword) .append(" ") .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 let head = match return_annotation { @@ -550,7 +561,7 @@ impl<'comments> Formatter<'comments> { return_annotation: Option<&'a Annotation>, body: &'a UntypedExpr, ) -> 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 { UntypedExpr::When { .. } => self.expr(body).force_break(), _ => self.expr(body), @@ -780,7 +791,10 @@ impl<'comments> Formatter<'comments> { UntypedExpr::Tuple { elems, .. } => "#" .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(), }; commented(document, comments) @@ -844,6 +858,7 @@ impl<'comments> Formatter<'comments> { _ => name .append(wrap_args( + false, args.iter().map(|a| (self.pattern_call_arg(a), is_record)), )) .group(), @@ -885,6 +900,7 @@ impl<'comments> Formatter<'comments> { _ => self .expr(fun) .append(wrap_args( + false, args.iter() .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 arg_docs = args.iter().map(|a| (self.record_update_arg(a), true)); 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>( @@ -1018,6 +1036,7 @@ impl<'comments> Formatter<'comments> { // x |> fun(_, 2, 3) self.expr(fun).append( wrap_args( + false, args.iter() .skip(1) .map(|a| (self.call_arg(a, false), false)), @@ -1026,8 +1045,9 @@ impl<'comments> Formatter<'comments> { ) } else { // x |> fun(1, _, 3) - self.expr(fun) - .append(wrap_args(args.iter().map(|a| (self.call_arg(a, false), false))).group()) + self.expr(fun).append( + 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( - 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,29 +1111,34 @@ impl<'comments> Formatter<'comments> { constructor .name .to_doc() - .append(wrap_args(constructor.arguments.iter().map( - |RecordConstructorArg { - label, - annotation, - location, - .. - }| { - let arg_comments = self.pop_comments(location.start); + .append(wrap_args( + false, + constructor.arguments.iter().map( + |RecordConstructorArg { + label, + annotation, + location, + .. + }| { + let arg_comments = self.pop_comments(location.start); - let arg = match label { - Some(l) => l.to_doc().append(": ").append(self.annotation(annotation)), - None => self.annotation(annotation), - }; + let arg = match label { + Some(l) => { + l.to_doc().append(": ").append(self.annotation(annotation)) + } + None => self.annotation(annotation), + }; - ( - commented( - self.doc_comments(location.start).append(arg).group(), - arg_comments, - ), - label.is_some(), - ) - }, - ))) + ( + commented( + self.doc_comments(location.start).append(arg).group(), + arg_comments, + ), + label.is_some(), + ) + }, + ), + )) .group() }; @@ -1247,30 +1272,72 @@ impl<'comments> Formatter<'comments> { &mut self, name: &'a str, args: &'a [TypedArg], + return_annotation: &'a Option, return_type: Arc, ) -> Document<'a> { - let mut printer = tipo::pretty::Printer::new(); - name.to_doc() - .append(self.docs_fn_args(args, &mut printer)) - .append(" -> ".to_doc()) - .append(printer.print(&return_type)) + let head = name + .to_doc() + .append(self.docs_fn_args(false, args)) + .append(" -> "); + + 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 - 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>) -> 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, - args: &'a [TypedArg], - printer: &mut tipo::pretty::Printer, + annotation: &'a Option, + type_info: &Arc, ) -> Document<'a> { - wrap_args(args.iter().map(|arg| { - ( - arg.arg_name - .to_doc() - .append(": ".to_doc().append(printer.print(&arg.tipo))) - .group(), - false, - ) - })) + match annotation { + Some(a) => self.annotation(a), + None => tipo::pretty::Printer::new().print(type_info), + } } fn wrap_expr<'a>(&mut self, expr: &'a UntypedExpr) -> Document<'a> { @@ -1403,7 +1470,10 @@ impl<'comments> Formatter<'comments> { Pattern::Tuple { elems, .. } => "#" .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(), 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 I: IntoIterator, bool)>, { @@ -1575,6 +1645,8 @@ where let (open_broken, open_unbroken, close) = if curly { (" {", " { ", "}") + } else if multiline { + ("( ", "( ", ")") } else { ("(", "(", ")") }; diff --git a/crates/project/src/docs.rs b/crates/project/src/docs.rs index a030ff7a..4517ed03 100644 --- a/crates/project/src/docs.rs +++ b/crates/project/src/docs.rs @@ -410,6 +410,7 @@ impl DocFunction { .docs_fn_signature( &func_def.name, &func_def.arguments, + &func_def.return_annotation, func_def.return_type.clone(), ) .to_pretty_string(MAX_COLUMNS),