mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 14:24:45 +00:00
Merge pull request #1261 from rtfeldman/module-level-documentation
Detatched Docs
This commit is contained in:
commit
84c46b94e2
4 changed files with 144 additions and 69 deletions
|
@ -1,8 +1,42 @@
|
||||||
interface Str
|
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 ]
|
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 []
|
imports []
|
||||||
## # Types
|
|
||||||
|
|
||||||
|
## # Types
|
||||||
|
##
|
||||||
## Dealing with text is a deep topic, so by design, Roc's `Str` module sticks
|
## Dealing with text is a deep topic, so by design, Roc's `Str` module sticks
|
||||||
## to the basics.
|
## to the basics.
|
||||||
##
|
##
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
|
use crate::docs::DocEntry::DetatchedDoc;
|
||||||
use crate::docs::TypeAnnotation::{Apply, BoundVariable, Record, TagUnion};
|
use crate::docs::TypeAnnotation::{Apply, BoundVariable, Record, TagUnion};
|
||||||
use inlinable_string::InlinableString;
|
use inlinable_string::InlinableString;
|
||||||
use roc_module::ident::ModuleName;
|
use roc_module::ident::ModuleName;
|
||||||
use roc_module::symbol::IdentIds;
|
use roc_module::symbol::IdentIds;
|
||||||
use roc_parse::ast;
|
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;
|
use roc_region::all::Located;
|
||||||
|
|
||||||
// Documentation generation requirements
|
// Documentation generation requirements
|
||||||
|
@ -19,12 +21,17 @@ pub struct Documentation {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ModuleDocumentation {
|
pub struct ModuleDocumentation {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub docs: String,
|
|
||||||
pub entries: Vec<DocEntry>,
|
pub entries: Vec<DocEntry>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct DocEntry {
|
pub enum DocEntry {
|
||||||
|
DocDef(DocDef),
|
||||||
|
DetatchedDoc(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct DocDef {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub type_vars: Vec<String>,
|
pub type_vars: Vec<String>,
|
||||||
pub type_annotation: Option<TypeAnnotation>,
|
pub type_annotation: Option<TypeAnnotation>,
|
||||||
|
@ -76,17 +83,40 @@ pub fn generate_module_docs<'a>(
|
||||||
parsed_defs
|
parsed_defs
|
||||||
.iter()
|
.iter()
|
||||||
.fold((vec![], None), |(acc, maybe_comments_after), def| {
|
.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 {
|
ModuleDocumentation {
|
||||||
name: module_name.as_str().to_string(),
|
name: module_name.as_str().to_string(),
|
||||||
docs: "".to_string(),
|
|
||||||
entries,
|
entries,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_module_doc<'a>(
|
fn detatched_docs_from_comments_and_new_lines<'a>(
|
||||||
|
comments_or_new_lines: &'a [roc_parse::ast::CommentOrNewline<'a>],
|
||||||
|
) -> Vec<String> {
|
||||||
|
let mut detatched_docs: Vec<String> = Vec::new();
|
||||||
|
|
||||||
|
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) => {
|
||||||
|
docs.push_str(doc_str);
|
||||||
|
docs.push('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
CommentOrNewline::LineComment(_) | CommentOrNewline::Newline => {
|
||||||
|
detatched_docs.push(docs.clone());
|
||||||
|
docs = String::new();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
detatched_docs
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_entry_doc<'a>(
|
||||||
exposed_ident_ids: &'a IdentIds,
|
exposed_ident_ids: &'a IdentIds,
|
||||||
mut acc: Vec<DocEntry>,
|
mut acc: Vec<DocEntry>,
|
||||||
before_comments_or_new_lines: Option<&'a [roc_parse::ast::CommentOrNewline<'a>]>,
|
before_comments_or_new_lines: Option<&'a [roc_parse::ast::CommentOrNewline<'a>]>,
|
||||||
|
@ -95,58 +125,62 @@ fn generate_module_doc<'a>(
|
||||||
Vec<DocEntry>,
|
Vec<DocEntry>,
|
||||||
Option<&'a [roc_parse::ast::CommentOrNewline<'a>]>,
|
Option<&'a [roc_parse::ast::CommentOrNewline<'a>]>,
|
||||||
) {
|
) {
|
||||||
use roc_parse::ast::Def::*;
|
|
||||||
use roc_parse::ast::Pattern;
|
use roc_parse::ast::Pattern;
|
||||||
|
|
||||||
match def {
|
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
|
// Comments before a definition are attached to the current defition
|
||||||
generate_module_doc(exposed_ident_ids, acc, Some(comments_or_new_lines), sub_def)
|
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
SpaceAfter(sub_def, comments_or_new_lines) => {
|
Def::SpaceAfter(sub_def, comments_or_new_lines) => {
|
||||||
let (new_acc, _) =
|
let (new_acc, _) =
|
||||||
// If there are comments before, attach to this definition
|
// 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
|
// Comments after a definition are attached to the next definition
|
||||||
(new_acc, Some(comments_or_new_lines))
|
(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) => {
|
Pattern::Identifier(identifier) => {
|
||||||
// Check if the definition is exposed
|
// Check if the definition is exposed
|
||||||
if exposed_ident_ids
|
if exposed_ident_ids
|
||||||
.get_id(&InlinableString::from(identifier))
|
.get_id(&InlinableString::from(identifier))
|
||||||
.is_some()
|
.is_some()
|
||||||
{
|
{
|
||||||
let entry = DocEntry {
|
let doc_def = DocDef {
|
||||||
name: identifier.to_string(),
|
name: identifier.to_string(),
|
||||||
type_annotation: None,
|
type_annotation: None,
|
||||||
type_vars: Vec::new(),
|
type_vars: Vec::new(),
|
||||||
docs: before_comments_or_new_lines.and_then(comments_or_new_lines_to_docs),
|
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)
|
(acc, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => (acc, None),
|
_ => (acc, None),
|
||||||
},
|
},
|
||||||
AnnotatedBody { ann_pattern, .. } => match ann_pattern.value {
|
Def::AnnotatedBody { ann_pattern, .. } => match ann_pattern.value {
|
||||||
Pattern::Identifier(identifier) => {
|
Pattern::Identifier(identifier) => {
|
||||||
// Check if the definition is exposed
|
// Check if the definition is exposed
|
||||||
if exposed_ident_ids
|
if exposed_ident_ids
|
||||||
.get_id(&InlinableString::from(identifier))
|
.get_id(&InlinableString::from(identifier))
|
||||||
.is_some()
|
.is_some()
|
||||||
{
|
{
|
||||||
let entry = DocEntry {
|
let doc_def = DocDef {
|
||||||
name: identifier.to_string(),
|
name: identifier.to_string(),
|
||||||
type_annotation: None,
|
type_annotation: None,
|
||||||
type_vars: Vec::new(),
|
type_vars: Vec::new(),
|
||||||
docs: before_comments_or_new_lines.and_then(comments_or_new_lines_to_docs),
|
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)
|
(acc, None)
|
||||||
}
|
}
|
||||||
|
@ -154,7 +188,7 @@ fn generate_module_doc<'a>(
|
||||||
_ => (acc, None),
|
_ => (acc, None),
|
||||||
},
|
},
|
||||||
|
|
||||||
Alias { name, vars, ann } => {
|
Def::Alias { name, vars, ann } => {
|
||||||
let mut type_vars = Vec::new();
|
let mut type_vars = Vec::new();
|
||||||
|
|
||||||
for var in vars.iter() {
|
for var in vars.iter() {
|
||||||
|
@ -163,22 +197,22 @@ fn generate_module_doc<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let entry = DocEntry {
|
let doc_def = DocDef {
|
||||||
name: name.value.to_string(),
|
name: name.value.to_string(),
|
||||||
type_annotation: type_to_docs(ann.value),
|
type_annotation: type_to_docs(ann.value),
|
||||||
type_vars,
|
type_vars,
|
||||||
docs: before_comments_or_new_lines.and_then(comments_or_new_lines_to_docs),
|
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)
|
(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 +361,20 @@ fn tag_to_doc(tag: ast::Tag) -> Option<Tag> {
|
||||||
fn comments_or_new_lines_to_docs<'a>(
|
fn comments_or_new_lines_to_docs<'a>(
|
||||||
comments_or_new_lines: &'a [roc_parse::ast::CommentOrNewline<'a>],
|
comments_or_new_lines: &'a [roc_parse::ast::CommentOrNewline<'a>],
|
||||||
) -> Option<String> {
|
) -> Option<String> {
|
||||||
use roc_parse::ast::CommentOrNewline::*;
|
|
||||||
|
|
||||||
let mut docs = String::new();
|
let mut docs = String::new();
|
||||||
|
|
||||||
for comment_or_new_line in comments_or_new_lines.iter() {
|
for comment_or_new_line in comments_or_new_lines.iter() {
|
||||||
match comment_or_new_line {
|
match comment_or_new_line {
|
||||||
DocComment(doc_str) => {
|
CommentOrNewline::DocComment(doc_str) => {
|
||||||
docs.push_str(doc_str);
|
docs.push_str(doc_str);
|
||||||
docs.push('\n');
|
docs.push('\n');
|
||||||
}
|
}
|
||||||
Newline | LineComment(_) => {
|
CommentOrNewline::Newline | CommentOrNewline::LineComment(_) => {
|
||||||
docs = String::new();
|
docs = String::new();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if docs.is_empty() {
|
if docs.is_empty() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -3516,7 +3516,6 @@ fn fabricate_effects_module<'a>(
|
||||||
// Should a effect module ever have a ModuleDocumentation?
|
// Should a effect module ever have a ModuleDocumentation?
|
||||||
let module_docs = ModuleDocumentation {
|
let module_docs = ModuleDocumentation {
|
||||||
name: String::from(name),
|
name: String::from(name),
|
||||||
docs: String::from("idk fix this later"),
|
|
||||||
entries: Vec::new(),
|
entries: Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
extern crate pulldown_cmark;
|
extern crate pulldown_cmark;
|
||||||
use roc_builtins::std::StdLib;
|
use roc_builtins::std::StdLib;
|
||||||
use roc_can::builtins::builtin_defs_map;
|
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::docs::{ModuleDocumentation, RecordField};
|
||||||
use roc_load::file::LoadingProblem;
|
use roc_load::file::LoadingProblem;
|
||||||
|
|
||||||
|
@ -86,41 +86,48 @@ fn render_main_content(module: &ModuleDocumentation) -> String {
|
||||||
.as_str(),
|
.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 {
|
for entry in &module.entries {
|
||||||
let mut href = String::new();
|
match entry {
|
||||||
href.push('#');
|
DocEntry::DocDef(doc_def) => {
|
||||||
href.push_str(entry.name.as_str());
|
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 {
|
for type_var in &doc_def.type_vars {
|
||||||
content.push(' ');
|
content.push(' ');
|
||||||
content.push_str(type_var.as_str());
|
content.push_str(type_var.as_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(type_ann) = &entry.type_annotation {
|
if let Some(type_ann) = &doc_def.type_annotation {
|
||||||
content.push_str(" : ");
|
content.push_str(" : ");
|
||||||
type_annotation_to_html(0, &mut content, &type_ann);
|
type_annotation_to_html(0, &mut content, &type_ann);
|
||||||
}
|
}
|
||||||
|
|
||||||
buf.push_str(
|
buf.push_str(
|
||||||
html_node(
|
html_node(
|
||||||
"h3",
|
"h3",
|
||||||
vec![("id", name), ("class", "entry-name")],
|
vec![("id", name), ("class", "entry-name")],
|
||||||
content.as_str(),
|
content.as_str(),
|
||||||
)
|
)
|
||||||
.as_str(),
|
.as_str(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(docs) = &entry.docs {
|
if let Some(docs) = &doc_def.docs {
|
||||||
buf.push_str(markdown_to_html(docs.to_string()).as_str());
|
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
|
buf
|
||||||
|
@ -218,20 +225,22 @@ fn render_module_links(modules: &[ModuleDocumentation]) -> String {
|
||||||
let mut entries_buf = String::new();
|
let mut entries_buf = String::new();
|
||||||
|
|
||||||
for entry in &module.entries {
|
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_str(href.as_str());
|
||||||
entry_href.push('#');
|
entry_href.push('#');
|
||||||
entry_href.push_str(entry.name.as_str());
|
entry_href.push_str(doc_def.name.as_str());
|
||||||
|
|
||||||
entries_buf.push_str(
|
entries_buf.push_str(
|
||||||
html_node(
|
html_node(
|
||||||
"a",
|
"a",
|
||||||
vec![("href", entry_href.as_str())],
|
vec![("href", entry_href.as_str())],
|
||||||
entry.name.as_str(),
|
doc_def.name.as_str(),
|
||||||
)
|
)
|
||||||
.as_str(),
|
.as_str(),
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
entries_buf
|
entries_buf
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue