Skip to content

Commit

Permalink
forc doc: Generate dependency documentation (FuelLabs#4546)
Browse files Browse the repository at this point in the history
## Description

Generates the documentation for dependencies & small improvements to the
API.

These changes **DO NOT** create links between dependency docs and the main docs, but generates the dependency docs and ensures there are no conflicts when generating these docs.

What's new so far:
- [x] Programs get their own folders, just like `cargo doc`
- [x] Generates documentation for dependencies by default: Closes FuelLabs#4533 
- [x] Fixes possible conflict with `assets` folder: Closes FuelLabs#4545 
- [x] Removes old docs before generating new ones: Closes FuelLabs#4544 

## Checklist

- [x] I have linked to any relevant issues. 
- [x] I have commented my code, particularly in hard-to-understand
areas.
- [x] I have updated the documentation where relevant (API docs, the
reference, and the Sway book).
- [ ] I have added tests that prove my fix is effective or that my
feature works.
- [x] I have added (or requested a maintainer to add) the necessary
`Breaking*` or `New Feature` labels where relevant.
- [x] I have done my best to ensure that my PR adheres to [the Fuel Labs
Code Review
Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md).
- [x] I have requested a review from the relevant team or maintainers.

---------

Co-authored-by: Chris O'Brien <[email protected]>
  • Loading branch information
eureka-cpu and Chris O'Brien authored May 22, 2023
1 parent bd31c91 commit e5d3185
Show file tree
Hide file tree
Showing 31 changed files with 295 additions and 181 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions forc-plugins/forc-doc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ repository.workspace = true
[dependencies]
anyhow = "1.0.65"
clap = { version = "4.0.18", features = ["derive"] }
colored = "2.0.0"
comrak = "0.16"
forc-pkg = { version = "0.39.0", path = "../../forc-pkg" }
forc-util = { version = "0.39.0", path = "../../forc-util" }
Expand Down
190 changes: 100 additions & 90 deletions forc-plugins/forc-doc/src/doc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,107 +6,57 @@ use anyhow::Result;
use std::option::Option;
use sway_core::{
decl_engine::DeclEngine,
language::ty::{TyAstNodeContent, TyDecl, TyImplTrait, TyProgram, TySubmodule},
language::ty::{TyAstNodeContent, TyDecl, TyImplTrait, TyModule, TyProgram, TySubmodule},
};
use sway_types::Spanned;

mod descriptor;
pub mod module;

pub(crate) type Documentation = Vec<Document>;
/// A finalized Document ready to be rendered. We want to retain all
/// information including spans, fields on structs, variants on enums etc.
#[derive(Clone, Debug)]
pub(crate) struct Document {
pub(crate) module_info: ModuleInfo,
pub(crate) item_header: ItemHeader,
pub(crate) item_body: ItemBody,
pub(crate) raw_attributes: Option<String>,
}
impl Document {
/// Creates an HTML file name from the [Document].
pub(crate) fn html_filename(&self) -> String {
use sway_core::language::ty::TyDecl::StorageDecl;
let name = match &self.item_body.ty_decl {
StorageDecl { .. } => None,
_ => Some(self.item_header.item_name.as_str()),
};

Document::create_html_filename(self.item_body.ty_decl.doc_name(), name)
}
fn create_html_filename(ty: &str, name: Option<&str>) -> String {
match name {
Some(name) => format!("{ty}.{name}.html"),
None => {
format!("{ty}.html") // storage does not have an Ident
}
}
}
/// Generate link info used in navigation between docs.
pub(crate) fn link(&self) -> DocLink {
DocLink {
name: self.item_header.item_name.as_str().to_owned(),
module_info: self.module_info.clone(),
html_filename: self.html_filename(),
preview_opt: self.preview_opt(),
}
}
fn preview_opt(&self) -> Option<String> {
create_preview(self.raw_attributes.clone())
}
#[derive(Default)]
pub(crate) struct Documentation(pub(crate) Vec<Document>);
impl Documentation {
/// Gather [Documentation] from the [TyProgram].
pub(crate) fn from_ty_program(
decl_engine: &DeclEngine,
project_name: &str,
typed_program: &TyProgram,
no_deps: bool,
document_private_items: bool,
) -> Result<Documentation> {
// the first module prefix will always be the project name
let mut docs: Documentation = Default::default();
let mut impl_traits: Vec<TyImplTrait> = Vec::new();
for ast_node in &typed_program.root.all_nodes {
if let TyAstNodeContent::Declaration(ref decl) = ast_node.content {
if let TyDecl::ImplTrait(impl_trait) = decl {
impl_traits.push(decl_engine.get_impl_trait(&impl_trait.decl_id))
} else {
let desc = Descriptor::from_typed_decl(
decl_engine,
decl,
ModuleInfo::from_ty_module(vec![project_name.to_owned()], None),
document_private_items,
)?;

if let Descriptor::Documentable(doc) = desc {
docs.push(doc)
}
}
}
}
let module_info = ModuleInfo::from_ty_module(vec![project_name.to_owned()], None);
Documentation::from_ty_module(
decl_engine,
module_info,
&typed_program.root,
&mut docs,
&mut impl_traits,
document_private_items,
)?;

if !no_deps && !typed_program.root.submodules.is_empty() {
// this is the same process as before but for dependencies
for (_, ref typed_submodule) in &typed_program.root.submodules {
let attributes = (!typed_submodule.module.attributes.is_empty())
.then(|| typed_submodule.module.attributes.to_html_string());
let module_prefix =
ModuleInfo::from_ty_module(vec![project_name.to_owned()], attributes);
Document::from_ty_submodule(
decl_engine,
typed_submodule,
&mut docs,
&mut impl_traits,
&module_prefix,
document_private_items,
)?;
}
// this is the same process as before but for submodules
for (_, ref typed_submodule) in &typed_program.root.submodules {
let attributes = (!typed_submodule.module.attributes.is_empty())
.then(|| typed_submodule.module.attributes.to_html_string());
let module_prefix =
ModuleInfo::from_ty_module(vec![project_name.to_owned()], attributes);
Documentation::from_ty_submodule(
decl_engine,
typed_submodule,
&mut docs,
&mut impl_traits,
&module_prefix,
document_private_items,
)?;
}

// match for the spans to add the impl_traits to their corresponding doc:
// currently this compares the spans as str, but this needs to change
// to compare the actual types
if !impl_traits.is_empty() {
for doc in &mut docs {
for doc in &mut docs.0 {
let mut impl_vec: Vec<TyImplTrait> = Vec::new();

match doc.item_body.ty_decl {
Expand All @@ -132,48 +82,108 @@ impl Document {

Ok(docs)
}
fn from_ty_submodule(
fn from_ty_module(
decl_engine: &DeclEngine,
typed_submodule: &TySubmodule,
module_info: ModuleInfo,
ty_module: &TyModule,
docs: &mut Documentation,
impl_traits: &mut Vec<TyImplTrait>,
module_prefix: &ModuleInfo,
document_private_items: bool,
) -> Result<()> {
let mut new_submodule_prefix = module_prefix.to_owned();
new_submodule_prefix
.module_prefixes
.push(typed_submodule.mod_name_span.as_str().to_owned());
for ast_node in &typed_submodule.module.all_nodes {
for ast_node in &ty_module.all_nodes {
if let TyAstNodeContent::Declaration(ref decl) = ast_node.content {
if let TyDecl::ImplTrait(impl_trait) = decl {
impl_traits.push(decl_engine.get_impl_trait(&impl_trait.decl_id))
} else {
let desc = Descriptor::from_typed_decl(
decl_engine,
decl,
new_submodule_prefix.clone(),
module_info.clone(),
document_private_items,
)?;

if let Descriptor::Documentable(doc) = desc {
docs.push(doc)
docs.0.push(doc)
}
}
}
}

Ok(())
}
fn from_ty_submodule(
decl_engine: &DeclEngine,
typed_submodule: &TySubmodule,
docs: &mut Documentation,
impl_traits: &mut Vec<TyImplTrait>,
module_info: &ModuleInfo,
document_private_items: bool,
) -> Result<()> {
let mut module_info = module_info.to_owned();
module_info
.module_prefixes
.push(typed_submodule.mod_name_span.as_str().to_owned());
Documentation::from_ty_module(
decl_engine,
module_info.clone(),
&typed_submodule.module,
docs,
impl_traits,
document_private_items,
)?;

for (_, submodule) in &typed_submodule.module.submodules {
Document::from_ty_submodule(
Documentation::from_ty_submodule(
decl_engine,
submodule,
docs,
impl_traits,
&new_submodule_prefix,
&module_info,
document_private_items,
)?;
}

Ok(())
}
}
/// A finalized Document ready to be rendered. We want to retain all
/// information including spans, fields on structs, variants on enums etc.
#[derive(Clone, Debug)]
pub(crate) struct Document {
pub(crate) module_info: ModuleInfo,
pub(crate) item_header: ItemHeader,
pub(crate) item_body: ItemBody,
pub(crate) raw_attributes: Option<String>,
}
impl Document {
/// Creates an HTML file name from the [Document].
pub(crate) fn html_filename(&self) -> String {
use sway_core::language::ty::TyDecl::StorageDecl;
let name = match &self.item_body.ty_decl {
StorageDecl { .. } => None,
_ => Some(self.item_header.item_name.as_str()),
};

Document::create_html_filename(self.item_body.ty_decl.doc_name(), name)
}
fn create_html_filename(ty: &str, name: Option<&str>) -> String {
match name {
Some(name) => format!("{ty}.{name}.html"),
None => {
format!("{ty}.html") // storage does not have an Ident
}
}
}
/// Generate link info used in navigation between docs.
pub(crate) fn link(&self) -> DocLink {
DocLink {
name: self.item_header.item_name.as_str().to_owned(),
module_info: self.module_info.clone(),
html_filename: self.html_filename(),
preview_opt: self.preview_opt(),
}
}
fn preview_opt(&self) -> Option<String> {
create_preview(self.raw_attributes.clone())
}
}
2 changes: 1 addition & 1 deletion forc-plugins/forc-doc/src/doc/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ impl ModuleInfo {
}
/// Create a path prefix `&str` for navigation from the `module.depth()`.
fn to_html_path_prefix(&self) -> String {
(1..self.depth()).map(|_| "../").collect::<String>()
(0..self.depth()).map(|_| "../").collect::<String>()
}
/// The depth of a module as `usize`.
pub(crate) fn depth(&self) -> usize {
Expand Down
2 changes: 1 addition & 1 deletion forc-plugins/forc-doc/src/licenses/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Assets Licenses Notice

This `licenses` directory contains all of the licenses associated with the assets contained within the [`assets`](../assets/) directory.
This `licenses` directory contains all of the licenses associated with the assets contained within the [`static.files`](../static.files/) directory.
Loading

0 comments on commit e5d3185

Please sign in to comment.