diff --git a/compiler/load/src/docs.rs b/compiler/load/src/docs.rs index 07933d0790..ae46aabdf3 100644 --- a/compiler/load/src/docs.rs +++ b/compiler/load/src/docs.rs @@ -1,3 +1,4 @@ +use crate::docs::DocEntry::DetatchedDoc; use crate::docs::TypeAnnotation::{Apply, BoundVariable, Record, TagUnion}; use inlinable_string::InlinableString; use roc_module::ident::ModuleName; @@ -20,12 +21,17 @@ pub struct Documentation { #[derive(Debug, Clone)] pub struct ModuleDocumentation { pub name: String, - pub docs: String, pub entries: Vec, } #[derive(Debug, Clone)] -pub struct DocEntry { +pub enum DocEntry { + DocDef(DocDef), + DetatchedDoc(String), +} + +#[derive(Debug, Clone)] +pub struct DocDef { pub name: String, pub type_vars: Vec, pub type_annotation: Option, @@ -73,10 +79,6 @@ pub fn generate_module_docs<'a>( exposed_ident_ids: &'a IdentIds, parsed_defs: &'a [Located>], ) -> ModuleDocumentation { - let maybe_module_docs = parsed_defs - .first() - .and_then(|first_def| generate_module_doc(&first_def.value)); - let (entries, _) = parsed_defs .iter() @@ -86,65 +88,32 @@ pub fn generate_module_docs<'a>( ModuleDocumentation { name: module_name.as_str().to_string(), - docs: match maybe_module_docs { - None => String::new(), - Some(module_docs) => module_docs, - }, entries, } } -fn generate_module_doc<'a>(def: &'a ast::Def<'a>) -> Option { - match def { - Def::SpaceBefore(_, comments_or_new_lines) => { - comments_or_new_lines_to_module_docs(comments_or_new_lines) - } - _ => None, - } -} - -fn comments_or_new_lines_to_module_docs<'a>( +fn detatched_docs_from_comments_and_new_lines<'a>( comments_or_new_lines: &'a [roc_parse::ast::CommentOrNewline<'a>], -) -> Option { - let mut docs = String::new(); +) -> Vec { + let mut detatched_docs: Vec = Vec::new(); - let mut in_doc_comments = false; - let mut left_doc_comments = false; + let mut docs = String::new(); for comment_or_new_line in comments_or_new_lines.iter() { match comment_or_new_line { CommentOrNewline::DocComment(doc_str) => { - in_doc_comments = true; docs.push_str(doc_str); docs.push('\n'); } CommentOrNewline::LineComment(_) | CommentOrNewline::Newline => { - if in_doc_comments { - left_doc_comments = true; - break; - } + detatched_docs.push(docs.clone()); + docs = String::new(); } } } - // We want to check if we ever traversed all the way through - // a block of doc comments, because in this case.. - // - // interface Box exposes [ new ] imports [] - // - // ## Make a box - // new : item -> Box item - // - // The doc comment "Make a box" belongs to the `new` function and - // not the module as a whole. The rule is that module documentation - // is the first block of doc comments that have regular comments or - // empty lines both before and after them. - if docs.is_empty() || !left_doc_comments { - None - } else { - Some(docs) - } + detatched_docs } fn generate_entry_doc<'a>( @@ -161,6 +130,11 @@ fn generate_entry_doc<'a>( match def { Def::SpaceBefore(sub_def, comments_or_new_lines) => { // Comments before a definition are attached to the current defition + + for detatched_doc in detatched_docs_from_comments_and_new_lines(comments_or_new_lines) { + acc.push(DetatchedDoc(detatched_doc)); + } + generate_entry_doc(exposed_ident_ids, acc, Some(comments_or_new_lines), sub_def) } @@ -180,13 +154,13 @@ fn generate_entry_doc<'a>( .get_id(&InlinableString::from(identifier)) .is_some() { - let entry = DocEntry { + let doc_def = DocDef { name: identifier.to_string(), type_annotation: None, type_vars: Vec::new(), docs: before_comments_or_new_lines.and_then(comments_or_new_lines_to_docs), }; - acc.push(entry); + acc.push(DocEntry::DocDef(doc_def)); } (acc, None) } @@ -200,13 +174,13 @@ fn generate_entry_doc<'a>( .get_id(&InlinableString::from(identifier)) .is_some() { - let entry = DocEntry { + let doc_def = DocDef { name: identifier.to_string(), type_annotation: None, type_vars: Vec::new(), docs: before_comments_or_new_lines.and_then(comments_or_new_lines_to_docs), }; - acc.push(entry); + acc.push(DocEntry::DocDef(doc_def)); } (acc, None) } @@ -223,13 +197,13 @@ fn generate_entry_doc<'a>( } } - let entry = DocEntry { + let doc_def = DocDef { name: name.value.to_string(), type_annotation: type_to_docs(ann.value), type_vars, docs: before_comments_or_new_lines.and_then(comments_or_new_lines_to_docs), }; - acc.push(entry); + acc.push(DocEntry::DocDef(doc_def)); (acc, None) } diff --git a/compiler/load/src/file.rs b/compiler/load/src/file.rs index 367bdb5161..3a86b4b77d 100644 --- a/compiler/load/src/file.rs +++ b/compiler/load/src/file.rs @@ -3516,7 +3516,6 @@ fn fabricate_effects_module<'a>( // Should a effect module ever have a ModuleDocumentation? let module_docs = ModuleDocumentation { name: String::from(name), - docs: String::from("idk fix this later"), entries: Vec::new(), }; diff --git a/docs/src/lib.rs b/docs/src/lib.rs index 50e63d6b6f..b5ab4347f5 100644 --- a/docs/src/lib.rs +++ b/docs/src/lib.rs @@ -1,7 +1,7 @@ extern crate pulldown_cmark; use roc_builtins::std::StdLib; use roc_can::builtins::builtin_defs_map; -use roc_load::docs::TypeAnnotation; +use roc_load::docs::{DocEntry, TypeAnnotation}; use roc_load::docs::{ModuleDocumentation, RecordField}; use roc_load::file::LoadingProblem; @@ -86,41 +86,48 @@ fn render_main_content(module: &ModuleDocumentation) -> String { .as_str(), ); - buf.push_str(markdown_to_html(module.docs.clone()).as_str()); + // buf.push_str(markdown_to_html(module.docs.clone()).as_str()); for entry in &module.entries { - let mut href = String::new(); - href.push('#'); - href.push_str(entry.name.as_str()); + match entry { + DocEntry::DocDef(doc_def) => { + let mut href = String::new(); + href.push('#'); + href.push_str(doc_def.name.as_str()); - let name = entry.name.as_str(); + let name = doc_def.name.as_str(); - let mut content = String::new(); + let mut content = String::new(); - content.push_str(html_node("a", vec![("href", href.as_str())], name).as_str()); + content.push_str(html_node("a", vec![("href", href.as_str())], name).as_str()); - for type_var in &entry.type_vars { - content.push(' '); - content.push_str(type_var.as_str()); - } + for type_var in &doc_def.type_vars { + content.push(' '); + content.push_str(type_var.as_str()); + } - if let Some(type_ann) = &entry.type_annotation { - content.push_str(" : "); - type_annotation_to_html(0, &mut content, &type_ann); - } + if let Some(type_ann) = &doc_def.type_annotation { + content.push_str(" : "); + type_annotation_to_html(0, &mut content, &type_ann); + } - buf.push_str( - html_node( - "h3", - vec![("id", name), ("class", "entry-name")], - content.as_str(), - ) - .as_str(), - ); + buf.push_str( + html_node( + "h3", + vec![("id", name), ("class", "entry-name")], + content.as_str(), + ) + .as_str(), + ); - if let Some(docs) = &entry.docs { - buf.push_str(markdown_to_html(docs.to_string()).as_str()); - } + if let Some(docs) = &doc_def.docs { + buf.push_str(markdown_to_html(docs.to_string()).as_str()); + } + } + DocEntry::DetatchedDoc(docs) => { + buf.push_str(markdown_to_html(docs.to_string()).as_str()); + } + }; } buf @@ -218,20 +225,22 @@ fn render_module_links(modules: &[ModuleDocumentation]) -> String { let mut entries_buf = String::new(); for entry in &module.entries { - let mut entry_href = String::new(); + if let DocEntry::DocDef(doc_def) = entry { + let mut entry_href = String::new(); - entry_href.push_str(href.as_str()); - entry_href.push('#'); - entry_href.push_str(entry.name.as_str()); + entry_href.push_str(href.as_str()); + entry_href.push('#'); + entry_href.push_str(doc_def.name.as_str()); - entries_buf.push_str( - html_node( - "a", - vec![("href", entry_href.as_str())], - entry.name.as_str(), - ) - .as_str(), - ); + entries_buf.push_str( + html_node( + "a", + vec![("href", entry_href.as_str())], + doc_def.name.as_str(), + ) + .as_str(), + ); + } } entries_buf