Skip to content

Commit

Permalink
chore(forc-doc): Reorganizes the file tree (FuelLabs#4382)
Browse files Browse the repository at this point in the history
## Description
Just a clean up PR to make finding & understanding things much easier.

## Checklist

- [x] I have linked to any relevant issues. FuelLabs#3656 FuelLabs#3659
- [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).
- [x] 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.
  • Loading branch information
eureka-cpu authored Apr 3, 2023
1 parent d6238ae commit 085175a
Show file tree
Hide file tree
Showing 20 changed files with 1,838 additions and 1,709 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
//! Determine whether a [Declaration] is documentable.
use crate::{
doc::{Document, ModuleInfo},
render::{trim_fn_body, Context, ContextType, DocStrings, ItemBody, ItemContext, ItemHeader},
doc::{module::ModuleInfo, Document},
render::{
item::{components::*, context::*},
util::format::{code_block::trim_fn_body, docstring::DocStrings},
},
};
use anyhow::Result;
use sway_core::{
Expand Down
137 changes: 137 additions & 0 deletions forc-plugins/forc-doc/src/doc/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
use crate::{
doc::{descriptor::Descriptor, module::ModuleInfo},
render::{item::components::*, link::DocLink, util::format::docstring::*},
};
use anyhow::Result;
use std::option::Option;
use sway_core::{
decl_engine::DeclEngine,
language::ty::{TyAstNodeContent, TyProgram, TySubmodule},
};

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())
}
/// 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();
for ast_node in &typed_program.root.all_nodes {
if let TyAstNodeContent::Declaration(ref decl) = ast_node.content {
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)
}
}
}

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,
&module_prefix,
document_private_items,
)?;
}
}

Ok(docs)
}
fn from_ty_submodule(
decl_engine: &DeclEngine,
typed_submodule: &TySubmodule,
docs: &mut Documentation,
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 {
if let TyAstNodeContent::Declaration(ref decl) = ast_node.content {
let desc = Descriptor::from_typed_decl(
decl_engine,
decl,
new_submodule_prefix.clone(),
document_private_items,
)?;

if let Descriptor::Documentable(doc) = desc {
docs.push(doc)
}
}
}
for (_, submodule) in &typed_submodule.module.submodules {
Document::from_ty_submodule(
decl_engine,
submodule,
docs,
&new_submodule_prefix,
document_private_items,
)?;
}

Ok(())
}
}
Original file line number Diff line number Diff line change
@@ -1,157 +1,11 @@
use crate::{
descriptor::Descriptor,
render::{
split_at_markdown_header, DocLink, DocStrings, ItemBody, ItemHeader, Renderable,
INDEX_FILENAME,
},
RenderPlan,
};
use crate::render::{constant::INDEX_FILENAME, util::format::docstring::create_preview};
use anyhow::Result;
use horrorshow::{box_html, RenderBox, Template};
use std::{fmt::Write, option::Option, path::PathBuf};
use sway_core::{
decl_engine::DeclEngine,
language::{
ty::{TyAstNodeContent, TyProgram, TySubmodule},
CallPath,
},
};

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())
}
/// 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();
for ast_node in &typed_program.root.all_nodes {
if let TyAstNodeContent::Declaration(ref decl) = ast_node.content {
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)
}
}
}

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,
&module_prefix,
document_private_items,
)?;
}
}

Ok(docs)
}
fn from_ty_submodule(
decl_engine: &DeclEngine,
typed_submodule: &TySubmodule,
docs: &mut Documentation,
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 {
if let TyAstNodeContent::Declaration(ref decl) = ast_node.content {
let desc = Descriptor::from_typed_decl(
decl_engine,
decl,
new_submodule_prefix.clone(),
document_private_items,
)?;

if let Descriptor::Documentable(doc) = desc {
docs.push(doc)
}
}
}
for (_, submodule) in &typed_submodule.module.submodules {
Document::from_ty_submodule(
decl_engine,
submodule,
docs,
&new_submodule_prefix,
document_private_items,
)?;
}

Ok(())
}
}
impl Renderable for Document {
fn render(self, render_plan: RenderPlan) -> Result<Box<dyn RenderBox>> {
let header = self.item_header.render(render_plan.clone())?;
let body = self.item_body.render(render_plan)?;
Ok(box_html! {
: header;
: body;
})
}
}
use horrorshow::{box_html, Template};
use std::{fmt::Write, path::PathBuf};
use sway_core::language::CallPath;

pub(crate) type ModulePrefixes = Vec<String>;

#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub(crate) struct ModuleInfo {
pub(crate) module_prefixes: ModulePrefixes,
Expand Down Expand Up @@ -333,35 +187,6 @@ impl ModuleInfo {
}
}

/// Create a docstring preview from raw html attributes.
///
/// Returns `None` if there are no attributes.
fn create_preview(raw_attributes: Option<String>) -> Option<String> {
const MAX_PREVIEW_CHARS: usize = 100;
const CLOSING_PARAGRAPH_TAG: &str = "</p>";

raw_attributes.as_ref().map(|description| {
let preview = split_at_markdown_header(description);
if preview.chars().count() > MAX_PREVIEW_CHARS && preview.contains(CLOSING_PARAGRAPH_TAG) {
let closing_tag_index = preview
.find(CLOSING_PARAGRAPH_TAG)
.expect("closing tag out of range");
// We add 1 here to get the index of the char after the closing tag.
// This ensures we retain the closing tag and don't break the html.
let (preview, _) =
preview.split_at(closing_tag_index + CLOSING_PARAGRAPH_TAG.len() + 1);
if preview.chars().count() > MAX_PREVIEW_CHARS && preview.contains('\n') {
let newline_index = preview.find('\n').expect("new line char out of range");
preview.split_at(newline_index).0.to_string()
} else {
preview.to_string()
}
} else {
preview.to_string()
}
})
}

#[cfg(test)]
mod tests {
use super::ModuleInfo;
Expand Down
13 changes: 6 additions & 7 deletions forc-plugins/forc-doc/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
mod cli;
mod descriptor;
mod doc;
mod render;

use crate::{
doc::{Document, Documentation},
render::{RenderedDocumentation, INDEX_FILENAME},
render::{constant::INDEX_FILENAME, RenderedDocumentation},
};
use anyhow::{bail, Result};
use clap::Parser;
Expand All @@ -21,6 +16,10 @@ use std::{
};
use sway_core::{decl_engine::DeclEngine, BuildTarget, Engines, TypeEngine};

mod cli;
mod doc;
mod render;

/// Information passed to the render phase to get TypeInfo, CallPath or visibility for type anchors.
#[derive(Clone)]
struct RenderPlan {
Expand Down Expand Up @@ -113,7 +112,7 @@ pub fn main() -> Result<()> {
.forc_version
.as_ref()
.map(|ver| format!("Forc v{}.{}.{}", ver.major, ver.minor, ver.patch));
let rendered_docs = RenderedDocumentation::from(
let rendered_docs = RenderedDocumentation::from_raw_docs(
raw_docs,
RenderPlan::new(
document_private_items,
Expand Down
Loading

0 comments on commit 085175a

Please sign in to comment.