From cf06eb8737092e7a3f43c49adf18028911867566 Mon Sep 17 00:00:00 2001 From: Chadtech Date: Sun, 2 May 2021 14:29:57 -0400 Subject: [PATCH 1/5] Add Bytes type to Bytes.roc --- compiler/builtins/docs/Bytes.roc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/builtins/docs/Bytes.roc b/compiler/builtins/docs/Bytes.roc index e93afe948b..9cb246fc99 100644 --- a/compiler/builtins/docs/Bytes.roc +++ b/compiler/builtins/docs/Bytes.roc @@ -15,10 +15,13 @@ interface Bytes parseU64, parseI64, parseU128, - parseI128, + parseI128 ] imports [] + +Bytes : [ @Bytes ] + # Conversion fromList : List U8 -> Bytes From 0e39675d6828d7e5fec0ff6135718aed40ebd66a Mon Sep 17 00:00:00 2001 From: Chadtech Date: Sun, 2 May 2021 14:31:17 -0400 Subject: [PATCH 2/5] Add Usv type to Str exports, and reformat Str interface generally --- compiler/builtins/docs/Str.roc | 44 +++++++++++++++++++++++++++++++--- compiler/builtins/src/std.rs | 1 + compiler/module/src/symbol.rs | 1 + 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/compiler/builtins/docs/Str.roc b/compiler/builtins/docs/Str.roc index f596eab552..4e4e294387 100644 --- a/compiler/builtins/docs/Str.roc +++ b/compiler/builtins/docs/Str.roc @@ -1,8 +1,46 @@ interface Str - exposes [ Str, decimal, split, isEmpty, startsWith, endsWith, contains, anyGraphemes, allGraphemes, join, joinWith, padGraphemesStart, padGraphemesEnd, graphemes, reverseGraphemes, isCaseInsensitiveEq, isCaseInsensitiveNeq, walkGraphemes, isCapitalized, isAllUppercase, isAllLowercase, toUtf8, toUtf16, toUtf32, trim, walkUtf8, walkUtf16, walkUtf32, walkRevUtf8, walkRevUtf16, walkRevUtf32 ] - imports [] -## # Types + exposes + [ + Str, + Usv, + decimal, + split, + isEmpty, + startsWith, + endsWith, + contains, + anyGraphemes, + allGraphemes, + join, + joinWith, + padGraphemesStart, + padGraphemesEnd, + graphemes, + reverseGraphemes, + isCaseInsensitiveEq, + isCaseInsensitiveNeq, + walkGraphemes, + isCapitalized, + isAllUppercase, + isAllLowercase, + toUtf8, + toUtf16, + toUtf32, + trim, + walkUtf8, + walkUtf16, + walkUtf32, + walkRevUtf8, + walkRevUtf16, + walkRevUtf32 + ] + imports + [ + Bytes.{ Bytes } + ] +## # Types +## ## Dealing with text is a deep topic, so by design, Roc's `Str` module sticks ## to the basics. ## diff --git a/compiler/builtins/src/std.rs b/compiler/builtins/src/std.rs index 480b0d2231..ed8c6e877d 100644 --- a/compiler/builtins/src/std.rs +++ b/compiler/builtins/src/std.rs @@ -48,6 +48,7 @@ pub fn standard_stdlib() -> StdLib { Symbol::SET_SET, Symbol::DICT_DICT, Symbol::STR_STR, + Symbol::STR_USV, ] .into_iter() .collect(), diff --git a/compiler/module/src/symbol.rs b/compiler/module/src/symbol.rs index 8a77a55e94..321baa5766 100644 --- a/compiler/module/src/symbol.rs +++ b/compiler/module/src/symbol.rs @@ -889,6 +889,7 @@ define_builtins! { 13 STR_UT8_PROBLEM: "Utf8Problem" // the Utf8Problem type alias 14 STR_UT8_BYTE_PROBLEM: "Utf8ByteProblem" // the Utf8ByteProblem type alias 15 STR_TO_BYTES: "toBytes" + 16 STR_USV: "Usv" imported // the Str.Usv type alias } 4 LIST: "List" => { 0 LIST_LIST: "List" imported // the List.List type alias From ad4402a67d1f1e4ceeba7cc963b605b525607e4b Mon Sep 17 00:00:00 2001 From: Chadtech Date: Sun, 2 May 2021 14:33:14 -0400 Subject: [PATCH 3/5] Figure out module documentation by reading through the comments or new lines from the SpaceBefore the first entry to the module --- compiler/load/src/docs.rs | 98 +++++++++++++++++++++++++++++++-------- 1 file changed, 79 insertions(+), 19 deletions(-) diff --git a/compiler/load/src/docs.rs b/compiler/load/src/docs.rs index 52ea66a45d..18321d7832 100644 --- a/compiler/load/src/docs.rs +++ b/compiler/load/src/docs.rs @@ -3,7 +3,8 @@ use inlinable_string::InlinableString; use roc_module::ident::ModuleName; use roc_module::symbol::IdentIds; use roc_parse::ast; -use roc_parse::ast::AssignedField; +use roc_parse::ast::CommentOrNewline; +use roc_parse::ast::{AssignedField, Def}; use roc_region::all::Located; // Documentation generation requirements @@ -72,21 +73,82 @@ 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() .fold((vec![], None), |(acc, maybe_comments_after), def| { - generate_module_doc(exposed_ident_ids, acc, maybe_comments_after, &def.value) + generate_entry_doc(exposed_ident_ids, acc, maybe_comments_after, &def.value) }); ModuleDocumentation { name: module_name.as_str().to_string(), - docs: "".to_string(), + docs: match maybe_module_docs { + None => String::new(), + Some(module_docs) => module_docs, + }, entries, } } -fn generate_module_doc<'a>( +fn generate_module_doc<'a>(def: &'a ast::Def<'a>) -> Option { + dbg!(def); + 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>( + comments_or_new_lines: &'a [roc_parse::ast::CommentOrNewline<'a>], +) -> Option { + let mut docs = String::new(); + + let mut in_doc_comments = false; + let mut left_doc_comments = false; + + 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; + } + } + } + } + + // 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) + } +} + +fn generate_entry_doc<'a>( exposed_ident_ids: &'a IdentIds, mut acc: Vec, before_comments_or_new_lines: Option<&'a [roc_parse::ast::CommentOrNewline<'a>]>, @@ -95,25 +157,24 @@ fn generate_module_doc<'a>( Vec, Option<&'a [roc_parse::ast::CommentOrNewline<'a>]>, ) { - use roc_parse::ast::Def::*; use roc_parse::ast::Pattern; match def { - SpaceBefore(sub_def, comments_or_new_lines) => { + Def::SpaceBefore(sub_def, comments_or_new_lines) => { // Comments before a definition are attached to the current defition - generate_module_doc(exposed_ident_ids, acc, Some(comments_or_new_lines), sub_def) + generate_entry_doc(exposed_ident_ids, acc, Some(comments_or_new_lines), sub_def) } - SpaceAfter(sub_def, comments_or_new_lines) => { + Def::SpaceAfter(sub_def, comments_or_new_lines) => { let (new_acc, _) = // If there are comments before, attach to this definition - generate_module_doc(exposed_ident_ids, acc, before_comments_or_new_lines, sub_def); + generate_entry_doc(exposed_ident_ids, acc, before_comments_or_new_lines, sub_def); // Comments after a definition are attached to the next definition (new_acc, Some(comments_or_new_lines)) } - Annotation(loc_pattern, _loc_ann) => match loc_pattern.value { + Def::Annotation(loc_pattern, _loc_ann) => match loc_pattern.value { Pattern::Identifier(identifier) => { // Check if the definition is exposed if exposed_ident_ids @@ -133,7 +194,7 @@ fn generate_module_doc<'a>( _ => (acc, None), }, - AnnotatedBody { ann_pattern, .. } => match ann_pattern.value { + Def::AnnotatedBody { ann_pattern, .. } => match ann_pattern.value { Pattern::Identifier(identifier) => { // Check if the definition is exposed if exposed_ident_ids @@ -154,7 +215,7 @@ fn generate_module_doc<'a>( _ => (acc, None), }, - Alias { name, vars, ann } => { + Def::Alias { name, vars, ann } => { let mut type_vars = Vec::new(); for var in vars.iter() { @@ -174,11 +235,11 @@ fn generate_module_doc<'a>( (acc, None) } - Body(_, _) => (acc, None), + Def::Body(_, _) => (acc, None), - Expect(c) => todo!("documentation for tests {:?}", c), + Def::Expect(c) => todo!("documentation for tests {:?}", c), - NotYetImplemented(s) => todo!("{}", s), + Def::NotYetImplemented(s) => todo!("{}", s), } } @@ -327,21 +388,20 @@ fn tag_to_doc(tag: ast::Tag) -> Option { fn comments_or_new_lines_to_docs<'a>( comments_or_new_lines: &'a [roc_parse::ast::CommentOrNewline<'a>], ) -> Option { - use roc_parse::ast::CommentOrNewline::*; - let mut docs = String::new(); for comment_or_new_line in comments_or_new_lines.iter() { match comment_or_new_line { - DocComment(doc_str) => { + CommentOrNewline::DocComment(doc_str) => { docs.push_str(doc_str); docs.push('\n'); } - Newline | LineComment(_) => { + CommentOrNewline::Newline | CommentOrNewline::LineComment(_) => { docs = String::new(); } } } + if docs.is_empty() { None } else { From 53c2a87161a34a1d347032c7599084875f1efaed Mon Sep 17 00:00:00 2001 From: Chadtech Date: Sun, 2 May 2021 14:38:00 -0400 Subject: [PATCH 4/5] Remove dbg --- compiler/load/src/docs.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/load/src/docs.rs b/compiler/load/src/docs.rs index 18321d7832..07933d0790 100644 --- a/compiler/load/src/docs.rs +++ b/compiler/load/src/docs.rs @@ -95,7 +95,6 @@ pub fn generate_module_docs<'a>( } fn generate_module_doc<'a>(def: &'a ast::Def<'a>) -> Option { - dbg!(def); match def { Def::SpaceBefore(_, comments_or_new_lines) => { comments_or_new_lines_to_module_docs(comments_or_new_lines) From d1a18c8b6ae4e01b4f3d588d3487d0f36d77cbaa Mon Sep 17 00:00:00 2001 From: Chadtech Date: Sun, 2 May 2021 16:34:48 -0400 Subject: [PATCH 5/5] Removed module docs and added detatched doc as a kind of doc entry --- compiler/load/src/docs.rs | 78 ++++++++++++----------------------- compiler/load/src/file.rs | 1 - docs/src/lib.rs | 87 +++++++++++++++++++++------------------ 3 files changed, 74 insertions(+), 92 deletions(-) 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