diff --git a/crates/lang/src/ast.rs b/crates/lang/src/ast.rs index ac9182ad..665e6588 100644 --- a/crates/lang/src/ast.rs +++ b/crates/lang/src/ast.rs @@ -130,14 +130,14 @@ impl TypedDataType { tipo: tipo.clone(), doc: None, }], - documentation: None, + doc: None, sugar: false, }, RecordConstructor { location: Span::empty(), name: "None".to_string(), arguments: vec![], - documentation: None, + doc: None, sugar: false, }, ], @@ -352,7 +352,7 @@ pub struct RecordConstructor { pub location: Span, pub name: String, pub arguments: Vec>, - pub documentation: Option, + pub doc: Option, pub sugar: bool, } diff --git a/crates/lang/src/format.rs b/crates/lang/src/format.rs index 5b6dfe72..aea70768 100644 --- a/crates/lang/src/format.rs +++ b/crates/lang/src/format.rs @@ -1172,37 +1172,93 @@ impl<'comments> Formatter<'comments> { .append("}") } - pub fn docs_opaque_custom_type<'a>( + pub fn docs_data_type<'a, A>( + &mut self, + name: &'a str, + args: &'a [String], + constructors: &'a [RecordConstructor], + location: &'a Span, + ) -> Document<'a> { + self.pop_empty_lines(location.start); + + let mut is_sugar = false; + + (if args.is_empty() { + name.to_doc() + } else { + name.to_doc() + .append(wrap_generics(args.iter().map(|e| e.to_doc()))) + .group() + }) + .append(" {") + .append(if constructors.len() == 1 && constructors[0].sugar { + is_sugar = true; + + self.record_constructor(&constructors[0]) + } else { + concat(constructors.iter().map(|c| { + if self.pop_empty_lines(c.location.start) { + lines(2) + } else { + line() + } + .append(self.record_constructor(c)) + .nest(INDENT) + .group() + })) + }) + .append(if is_sugar { nil() } else { line() }) + .append("}") + } + + pub fn docs_opaque_data_type<'a>( &mut self, - public: bool, name: &'a str, args: &'a [String], location: &'a Span, ) -> Document<'a> { self.pop_empty_lines(location.start); - pub_(public) - .to_doc() - .append("opaque type ") - .append(if args.is_empty() { - name.to_doc() - } else { - name.to_doc() - .append(wrap_args(args.iter().map(|e| (e.to_doc(), false)))) - }) + if args.is_empty() { + name.to_doc() + } else { + name.to_doc() + .append(wrap_args(args.iter().map(|e| (e.to_doc(), false)))) + } + } + + pub fn docs_type_alias<'a>( + &mut self, + name: &'a str, + args: &'a [String], + typ: &'a Annotation, + ) -> Document<'a> { + let head = name.to_doc(); + + let head = if args.is_empty() { + head + } else { + head.append(wrap_generics(args.iter().map(|e| e.to_doc())).group()) + }; + + head.append(" = ") + .append(self.annotation(typ).group().nest(INDENT)) + } + + pub fn docs_record_constructor<'a, A>( + &mut self, + constructor: &'a RecordConstructor, + ) -> Document<'a> { + constructor.name.to_doc() } pub fn docs_fn_signature<'a>( &mut self, - public: bool, name: &'a str, args: &'a [TypedArg], return_type: Arc, ) -> Document<'a> { let mut printer = tipo::pretty::Printer::new(); - - pub_(public) - .append("fn ") - .append(name) + name.to_doc() .append(self.docs_fn_args(args, &mut printer)) .append(" -> ".to_doc()) .append(printer.print(&return_type)) diff --git a/crates/lang/src/parser.rs b/crates/lang/src/parser.rs index 025e08ea..cc11d850 100644 --- a/crates/lang/src/parser.rs +++ b/crates/lang/src/parser.rs @@ -157,7 +157,7 @@ pub fn data_parser() -> impl Parser impl Parser) -> Vec { let timestamp = new_timestamp(); + let modules_links = generate_modules_links(&modules); let mut output_files: Vec = vec![]; let mut search_indexes: Vec = vec![]; for module in &modules { - let (indexes, file) = generate_module(config, module, ×tamp); + let (indexes, file) = generate_module(config, module, &modules_links, ×tamp); search_indexes.extend(indexes); output_files.push(file); } output_files.extend(generate_static_assets(search_indexes)); - output_files.push(generate_readme(root, config, modules, ×tamp)); + output_files.push(generate_readme(root, config, &modules_links, ×tamp)); output_files } fn generate_module( config: &Config, module: &CheckedModule, + modules: &Vec, timestamp: &Duration, ) -> (Vec, DocFile) { let mut search_indexes = vec![]; @@ -116,11 +120,44 @@ fn generate_module( }); // Types + let types: Vec = module + .ast + .definitions + .iter() + .flat_map(DocType::from_definition) + .sorted() + .collect(); + types.iter().for_each(|type_info| { + let constructors = type_info + .constructors + .iter() + .map(|constructor| { + let arguments = constructor + .arguments + .iter() + .map(|argument| format!("{}\n{}", argument.label, argument.documentation)) + .join("\n"); + format!( + "{}\n{}\n{}", + constructor.definition, constructor.documentation, arguments + ) + }) + .join("\n"); + search_indexes.push(SearchIndex { + doc: module.name.to_string(), + title: type_info.name.to_string(), + content: format!( + "{}\n{}\n{}", + type_info.definition, type_info.documentation, constructors, + ), + url: format!("{}.html#{}", module.name, type_info.name), + }) + }); // Constants + // TODO - // Template - + // Module search_indexes.push(SearchIndex { doc: module.name.to_string(), title: module.name.to_string(), @@ -133,13 +170,13 @@ fn generate_module( breadcrumbs: to_breadcrumbs(&module.name), links: &vec![], documentation: render_markdown(&module.ast.docs.iter().join("\n")), - modules: &vec![], + modules, project_name: &config.name, page_title: &format!("{} - {}", module.name, config.name), module_name: module.name.clone(), project_version: &config.version.to_string(), functions, - types: vec![], + types, constants: vec![], timestamp: timestamp.as_secs().to_string(), }; @@ -208,28 +245,18 @@ fn generate_static_assets(search_indexes: Vec) -> Vec { fn generate_readme( root: &PathBuf, config: &Config, - modules: Vec<&CheckedModule>, + modules: &Vec, timestamp: &Duration, ) -> DocFile { let path = PathBuf::from("index.html"); let content = std::fs::read_to_string(root.join("README.md")).unwrap_or_default(); - let mut modules_links = vec![]; - for module in modules { - let module_path = [&module.name.clone(), ".html"].concat(); - modules_links.push(DocLink { - path: module_path, - name: module.name.to_string().clone(), - }); - } - modules_links.sort(); - let template = PageTemplate { aiken_version: VERSION, breadcrumbs: ".", links: &vec![], - modules: &modules_links, + modules, project_name: &config.name, page_title: &config.name, project_version: &config.version.to_string(), @@ -243,6 +270,19 @@ fn generate_readme( } } +fn generate_modules_links(modules: &Vec<&CheckedModule>) -> Vec { + let mut modules_links = vec![]; + for module in modules { + let module_path = [&module.name.clone(), ".html"].concat(); + modules_links.push(DocLink { + path: module_path, + name: module.name.to_string().clone(), + }); + } + modules_links.sort(); + modules_links +} + #[derive(PartialEq, Eq, PartialOrd, Ord)] struct DocFunction { name: String, @@ -263,13 +303,12 @@ impl DocFunction { .unwrap_or_default(), signature: format::Formatter::new() .docs_fn_signature( - true, &func_def.name, &func_def.arguments, func_def.return_type.clone(), ) .to_pretty_string(MAX_COLUMNS), - source_url: "TODO: source_url".to_string(), + source_url: "#todo".to_string(), }), _ => None, } @@ -281,7 +320,6 @@ struct DocConstant { name: String, definition: String, documentation: String, - text_documentation: String, source_url: String, } @@ -291,22 +329,99 @@ struct DocType { definition: String, documentation: String, constructors: Vec, - text_documentation: String, source_url: String, } +impl DocType { + fn from_definition(def: &TypedDefinition) -> Option { + match def { + Definition::TypeAlias(info) if info.public => Some(DocType { + name: info.alias.clone(), + definition: format::Formatter::new() + .docs_type_alias(&info.alias, &info.parameters, &info.annotation) + .to_pretty_string(MAX_COLUMNS), + documentation: info.doc.as_deref().map(render_markdown).unwrap_or_default(), + constructors: vec![], + source_url: "#todo".to_string(), + }), + + Definition::DataType(info) if info.public && !info.opaque => Some(DocType { + name: info.name.clone(), + definition: format::Formatter::new() + .docs_data_type( + &info.name, + &info.parameters, + &info.constructors, + &info.location, + ) + .to_pretty_string(MAX_COLUMNS), + documentation: info.doc.as_deref().map(render_markdown).unwrap_or_default(), + constructors: info + .constructors + .iter() + .map(DocTypeConstructor::from_record_constructor) + .collect(), + source_url: "#todo".to_string(), + }), + + Definition::DataType(info) if info.public && info.opaque => Some(DocType { + name: info.name.clone(), + definition: format::Formatter::new() + .docs_opaque_data_type(&info.name, &info.parameters, &info.location) + .to_pretty_string(MAX_COLUMNS), + documentation: info.doc.as_deref().map(render_markdown).unwrap_or_default(), + constructors: vec![], + source_url: "#todo".to_string(), + }), + + _ => None, + } + } +} + #[derive(PartialEq, Eq, PartialOrd, Ord, Debug)] struct DocTypeConstructor { definition: String, documentation: String, - text_documentation: String, arguments: Vec, } +impl DocTypeConstructor { + fn from_record_constructor(constructor: &RecordConstructor>) -> Self { + DocTypeConstructor { + definition: format::Formatter::new() + .docs_record_constructor(constructor) + .to_pretty_string(MAX_COLUMNS), + documentation: constructor + .doc + .as_deref() + .map(render_markdown) + .unwrap_or_default(), + arguments: constructor + .arguments + .iter() + .filter_map(DocTypeConstructorArg::from_record_constructor_arg) + .collect(), + } + } +} + #[derive(PartialEq, Eq, PartialOrd, Ord, Debug)] struct DocTypeConstructorArg { - name: String, - doc: String, + label: String, + documentation: String, +} + +impl DocTypeConstructorArg { + fn from_record_constructor_arg(arg: &RecordConstructorArg>) -> Option { + match &arg.label { + None => None, + Some(label) => Some(DocTypeConstructorArg { + label: label.clone(), + documentation: arg.doc.as_deref().map(render_markdown).unwrap_or_default(), + }), + } + } } // ------ Extra Helpers diff --git a/crates/project/templates/module.html b/crates/project/templates/module.html index 7f127e79..ad5bf69f 100644 --- a/crates/project/templates/module.html +++ b/crates/project/templates/module.html @@ -83,10 +83,10 @@
  • - {{ argument.name }} + {{ argument.label }}

    - {{ argument.doc|safe }} + {{ argument.documentation|safe }}