Render docs for abilities, tuples, and as

This commit is contained in:
Richard Feldman 2023-08-11 20:59:40 -04:00
parent 0fea1ec227
commit 9fe08cafd0
No known key found for this signature in database
GPG Key ID: F1F21AA5B1D9E43B
3 changed files with 199 additions and 53 deletions

View File

@ -54,11 +54,30 @@ pub enum TypeAnnotation {
fields: Vec<RecordField>,
extension: Box<TypeAnnotation>,
},
Tuple {
elems: Vec<TypeAnnotation>,
extension: Box<TypeAnnotation>,
},
Ability {
members: Vec<AbilityMember>,
},
Wildcard,
NoTypeAnn,
Where {
ann: Box<TypeAnnotation>,
implements: Vec<ImplementsClause>,
},
As {
ann: Box<TypeAnnotation>,
name: String,
vars: Vec<String>,
},
}
#[derive(Debug, Clone)]
pub struct ImplementsClause {
pub name: String,
pub abilities: Vec<TypeAnnotation>,
}
#[derive(Debug, Clone)]
@ -540,7 +559,57 @@ fn type_to_docs(in_func_type_ann: bool, type_annotation: ast::TypeAnnotation) ->
}
}
ast::TypeAnnotation::Wildcard => TypeAnnotation::Wildcard,
_ => NoTypeAnn,
ast::TypeAnnotation::As(loc_ann, _comments, type_header) => TypeAnnotation::As {
ann: Box::new(type_to_docs(in_func_type_ann, loc_ann.value)),
name: type_header.name.value.to_string(),
vars: type_header
.vars
.iter()
.filter_map(|loc_pattern| match loc_pattern.value {
ast::Pattern::Identifier(ident) => Some(ident.to_string()),
_ => None,
})
.collect(),
},
ast::TypeAnnotation::Tuple { elems, ext } => {
let mut doc_elems = Vec::new();
for loc_ann in elems.items {
doc_elems.push(type_to_docs(in_func_type_ann, loc_ann.value));
}
let extension = match ext {
None => NoTypeAnn,
Some(ext_type_ann) => type_to_docs(in_func_type_ann, ext_type_ann.value),
};
TypeAnnotation::Tuple {
elems: doc_elems,
extension: Box::new(extension),
}
}
ast::TypeAnnotation::Where(loc_ann, implements) => TypeAnnotation::Where {
ann: Box::new(type_to_docs(in_func_type_ann, loc_ann.value)),
implements: implements
.iter()
.map(|clause| {
let abilities = clause
.value
.abilities
.iter()
.map(|ability| type_to_docs(in_func_type_ann, ability.value))
.collect();
ImplementsClause {
name: clause.value.var.value.item().to_string(),
abilities,
}
})
.collect(),
},
ast::TypeAnnotation::Malformed(_) | ast::TypeAnnotation::Inferred => {
TypeAnnotation::NoTypeAnn
}
}
}

View File

@ -38,6 +38,13 @@ impl<'a, T> Spaced<'a, T> {
}
}
}
pub fn item(&self) -> &T {
match self {
Spaced::Item(answer) => answer,
Spaced::SpaceBefore(next, _spaces) | Spaced::SpaceAfter(next, _spaces) => next.item(),
}
}
}
impl<'a, T: Debug> Debug for Spaced<'a, T> {

View File

@ -11,6 +11,7 @@ use roc_load::{ExecutionMode, LoadConfig, LoadedModule, LoadingProblem, Threadin
use roc_module::symbol::{Interns, Symbol};
use roc_packaging::cache::{self, RocCacheDir};
use roc_parse::ident::{parse_ident, Accessor, Ident};
use roc_parse::keyword;
use roc_parse::state::State;
use roc_region::all::Region;
use std::fs;
@ -626,6 +627,7 @@ fn type_annotation_to_html(
TypeAnnotation::Function { args, output } => {
let mut paren_is_open = false;
let mut peekable_args = args.iter().peekable();
while let Some(arg) = peekable_args.next() {
if is_multiline {
if !should_be_multiline(arg) {
@ -638,8 +640,7 @@ fn type_annotation_to_html(
paren_is_open = true;
}
let child_needs_parens =
matches!(arg, TypeAnnotation::Function { args: _, output: _ });
let child_needs_parens = matches!(arg, TypeAnnotation::Function { .. });
type_annotation_to_html(indent_level, buf, arg, child_needs_parens);
if peekable_args.peek().is_some() {
@ -676,77 +677,146 @@ fn type_annotation_to_html(
}
TypeAnnotation::NoTypeAnn => {}
TypeAnnotation::Wildcard => buf.push('*'),
TypeAnnotation::Tuple { elems, extension } => {
let elems_len = elems.len();
let tuple_indent = indent_level + 1;
if is_multiline {
new_line(buf);
indent(buf, tuple_indent);
}
buf.push('(');
if is_multiline {
new_line(buf);
}
let next_indent_level = tuple_indent + 1;
for (index, elem) in elems.iter().enumerate() {
if is_multiline {
indent(buf, next_indent_level);
}
type_annotation_to_html(next_indent_level, buf, elem, false);
if is_multiline {
if index < (elems_len - 1) {
buf.push(',');
}
new_line(buf);
}
}
if is_multiline {
indent(buf, tuple_indent);
}
buf.push(')');
type_annotation_to_html(indent_level, buf, extension, true);
}
TypeAnnotation::Where { ann, implements } => {
type_annotation_to_html(indent_level, buf, ann, false);
new_line(buf);
indent(buf, indent_level + 1);
buf.push_str(keyword::WHERE);
let multiline_implements = implements
.iter()
.any(|imp| imp.abilities.iter().any(should_be_multiline));
for (index, imp) in implements.iter().enumerate() {
if index != 0 {
buf.push(',');
}
if multiline_implements {
new_line(buf);
indent(buf, indent_level + 2);
} else {
buf.push(' ')
}
buf.push_str(&imp.name);
buf.push(' ');
buf.push_str(keyword::IMPLEMENTS);
buf.push(' ');
for (index, ability) in imp.abilities.iter().enumerate() {
if index != 0 {
buf.push_str(" & ");
}
type_annotation_to_html(indent_level, buf, ability, false);
}
}
}
TypeAnnotation::As { ann, name, vars } => {
type_annotation_to_html(indent_level, buf, ann, true);
buf.push(' ');
buf.push_str(name);
for var in vars {
buf.push(' ');
buf.push_str(var);
}
}
}
}
fn should_be_multiline(type_ann: &TypeAnnotation) -> bool {
match type_ann {
TypeAnnotation::TagUnion { tags, extension } => {
let mut is_multiline = should_be_multiline(extension) || tags.len() > 1;
for tag in tags {
for value in &tag.values {
if is_multiline {
break;
}
is_multiline = should_be_multiline(value);
}
}
is_multiline
tags.len() > 1
|| should_be_multiline(extension)
|| tags
.iter()
.any(|tag| tag.values.iter().any(should_be_multiline))
}
TypeAnnotation::Function { args, output } => {
let mut is_multiline = should_be_multiline(output) || args.len() > 2;
for arg in args {
if is_multiline {
break;
}
is_multiline = should_be_multiline(arg);
}
is_multiline
args.len() > 2 || should_be_multiline(output) || args.iter().any(should_be_multiline)
}
TypeAnnotation::ObscuredTagUnion => false,
TypeAnnotation::ObscuredRecord => false,
TypeAnnotation::BoundVariable(_) => false,
TypeAnnotation::Apply { parts, .. } => {
let mut is_multiline = false;
for part in parts {
is_multiline = should_be_multiline(part);
if is_multiline {
break;
}
}
is_multiline
}
TypeAnnotation::Apply { parts, .. } => parts.iter().any(should_be_multiline),
TypeAnnotation::Record { fields, extension } => {
let mut is_multiline = should_be_multiline(extension) || fields.len() > 1;
for field in fields {
if is_multiline {
break;
}
match field {
fields.len() > 1
|| should_be_multiline(extension)
|| fields.iter().any(|field| match field {
RecordField::RecordField {
type_annotation, ..
} => is_multiline = should_be_multiline(type_annotation),
} => should_be_multiline(type_annotation),
RecordField::OptionalField {
type_annotation, ..
} => is_multiline = should_be_multiline(type_annotation),
RecordField::LabelOnly { .. } => {}
}
}
is_multiline
} => should_be_multiline(type_annotation),
RecordField::LabelOnly { .. } => false,
})
}
TypeAnnotation::Ability { .. } => true,
TypeAnnotation::Wildcard => false,
TypeAnnotation::NoTypeAnn => false,
TypeAnnotation::Tuple { elems, extension } => {
elems.len() > 1
|| should_be_multiline(extension)
|| elems.iter().any(should_be_multiline)
}
TypeAnnotation::Where { ann, implements } => {
should_be_multiline(ann)
|| implements
.iter()
.any(|imp| imp.abilities.iter().any(should_be_multiline))
}
TypeAnnotation::As {
ann,
name: _,
vars: _,
} => should_be_multiline(ann),
}
}