Skip to content

Commit

Permalink
Add links for types that go to their definitions (FuelLabs#4017)
Browse files Browse the repository at this point in the history
This is nearly complete, but there are a few things I need to address
before this can go through: FuelLabs#4021 (ref FuelLabs#3744) & FuelLabs#4018

Closes FuelLabs#3173 

[Screencast from 2023-02-13
12-32-28.webm](https://user-images.githubusercontent.com/57543709/218544317-00634be0-86de-4e0c-b2fc-97ef9f00c41a.webm)

---------

Co-authored-by: Joshua Batty <[email protected]>
  • Loading branch information
eureka-cpu and JoshuaBatty authored Mar 4, 2023
1 parent 0125b38 commit 276ef41
Show file tree
Hide file tree
Showing 4 changed files with 537 additions and 240 deletions.
75 changes: 47 additions & 28 deletions forc-plugins/forc-doc/src/descriptor.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Determine whether a [Declaration] is documentable.
use crate::{
doc::{Document, ModuleInfo},
render::{trim_fn_body, ContextType, DocStrings, ItemBody, ItemContext, ItemHeader},
render::{trim_fn_body, Context, ContextType, DocStrings, ItemBody, ItemContext, ItemHeader},
};
use anyhow::Result;
use sway_core::{
Expand Down Expand Up @@ -47,8 +47,10 @@ impl Descriptor {
let item_name = struct_decl.call_path.suffix;
let attrs_opt = (!struct_decl.attributes.is_empty())
.then(|| struct_decl.attributes.to_html_string());
let context = (!struct_decl.fields.is_empty())
.then_some(ContextType::StructFields(struct_decl.fields));
let context = (!struct_decl.fields.is_empty()).then_some(Context::new(
module_info.clone(),
ContextType::StructFields(struct_decl.fields),
));

Ok(Descriptor::Documentable(Document {
module_info: module_info.clone(),
Expand All @@ -65,7 +67,9 @@ impl Descriptor {
struct_decl.span.as_str(),
),
attrs_opt: attrs_opt.clone(),
item_context: ItemContext { context },
item_context: ItemContext {
context_opt: context,
},
},
raw_attributes: attrs_opt,
}))
Expand All @@ -79,8 +83,10 @@ impl Descriptor {
let item_name = enum_decl.call_path.suffix;
let attrs_opt = (!enum_decl.attributes.is_empty())
.then(|| enum_decl.attributes.to_html_string());
let context = (!enum_decl.variants.is_empty())
.then_some(ContextType::EnumVariants(enum_decl.variants));
let context = (!enum_decl.variants.is_empty()).then_some(Context::new(
module_info.clone(),
ContextType::EnumVariants(enum_decl.variants),
));

Ok(Descriptor::Documentable(Document {
module_info: module_info.clone(),
Expand All @@ -97,7 +103,9 @@ impl Descriptor {
enum_decl.span.as_str(),
),
attrs_opt: attrs_opt.clone(),
item_context: ItemContext { context },
item_context: ItemContext {
context_opt: context,
},
},
raw_attributes: attrs_opt,
}))
Expand All @@ -111,18 +119,20 @@ impl Descriptor {
let item_name = trait_decl.name;
let attrs_opt = (!trait_decl.attributes.is_empty())
.then(|| trait_decl.attributes.to_html_string());
let context = (!trait_decl.interface_surface.is_empty()).then_some(
ContextType::RequiredMethods(
trait_decl
.interface_surface
.into_iter()
.flat_map(|item| match item {
TyTraitInterfaceItem::TraitFn(fn_decl) => Some(fn_decl),
})
.collect::<Vec<_>>()
.to_methods(decl_engine),
),
);
let context =
(!trait_decl.interface_surface.is_empty()).then_some(Context::new(
module_info.clone(),
ContextType::RequiredMethods(
trait_decl
.interface_surface
.into_iter()
.flat_map(|item| match item {
TyTraitInterfaceItem::TraitFn(fn_decl) => Some(fn_decl),
})
.collect::<Vec<_>>()
.to_methods(decl_engine),
),
));

Ok(Descriptor::Documentable(Document {
module_info: module_info.clone(),
Expand All @@ -139,7 +149,9 @@ impl Descriptor {
trait_decl.span.as_str(),
),
attrs_opt: attrs_opt.clone(),
item_context: ItemContext { context },
item_context: ItemContext {
context_opt: context,
},
},
raw_attributes: attrs_opt,
}))
Expand All @@ -150,7 +162,8 @@ impl Descriptor {
let item_name = abi_decl.name;
let attrs_opt =
(!abi_decl.attributes.is_empty()).then(|| abi_decl.attributes.to_html_string());
let context = (!abi_decl.interface_surface.is_empty()).then_some(
let context = (!abi_decl.interface_surface.is_empty()).then_some(Context::new(
module_info.clone(),
ContextType::RequiredMethods(
abi_decl
.interface_surface
Expand All @@ -161,7 +174,7 @@ impl Descriptor {
.collect::<Vec<_>>()
.to_methods(decl_engine),
),
);
));

Ok(Descriptor::Documentable(Document {
module_info: module_info.clone(),
Expand All @@ -176,7 +189,9 @@ impl Descriptor {
item_name,
code_str: parse::parse_format::<sway_ast::ItemAbi>(abi_decl.span.as_str()),
attrs_opt: attrs_opt.clone(),
item_context: ItemContext { context },
item_context: ItemContext {
context_opt: context,
},
},
raw_attributes: attrs_opt,
}))
Expand All @@ -188,8 +203,10 @@ impl Descriptor {
);
let attrs_opt = (!storage_decl.attributes.is_empty())
.then(|| storage_decl.attributes.to_html_string());
let context = (!storage_decl.fields.is_empty())
.then_some(ContextType::StorageFields(storage_decl.fields));
let context = (!storage_decl.fields.is_empty()).then_some(Context::new(
module_info.clone(),
ContextType::StorageFields(storage_decl.fields),
));

Ok(Descriptor::Documentable(Document {
module_info: module_info.clone(),
Expand All @@ -206,7 +223,9 @@ impl Descriptor {
storage_decl.span.as_str(),
),
attrs_opt: attrs_opt.clone(),
item_context: ItemContext { context },
item_context: ItemContext {
context_opt: context,
},
},
raw_attributes: attrs_opt,
}))
Expand Down Expand Up @@ -263,7 +282,7 @@ impl Descriptor {
fn_decl.span.as_str(),
)),
attrs_opt: attrs_opt.clone(),
item_context: ItemContext { context: None },
item_context: ItemContext { context_opt: None },
},
raw_attributes: attrs_opt,
}))
Expand Down Expand Up @@ -293,7 +312,7 @@ impl Descriptor {
const_decl.span.as_str(),
),
attrs_opt: attrs_opt.clone(),
item_context: ItemContext { context: None },
item_context: ItemContext { context_opt: None },
},
raw_attributes: attrs_opt,
}))
Expand Down
71 changes: 62 additions & 9 deletions forc-plugins/forc-doc/src/doc.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
use crate::{
descriptor::Descriptor,
render::{split_at_markdown_header, DocLink, DocStrings, ItemBody, ItemHeader, Renderable},
RenderPlan,
};
use anyhow::Result;
use horrorshow::{box_html, RenderBox};
use std::option::Option;
use std::path::PathBuf;
use std::{fmt::Write, option::Option, path::PathBuf};
use sway_core::{
decl_engine::DeclEngine,
language::ty::{TyAstNodeContent, TyProgram, TySubmodule},
language::{
ty::{TyAstNodeContent, TyProgram, TySubmodule},
CallPath,
},
};

pub(crate) type Documentation = Vec<Document>;
Expand Down Expand Up @@ -136,9 +139,9 @@ impl Document {
}
}
impl Renderable for Document {
fn render(self) -> Result<Box<dyn RenderBox>> {
let header = self.item_header.render()?;
let body = self.item_body.render()?;
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;
Expand Down Expand Up @@ -213,10 +216,10 @@ impl ModuleInfo {
iter.map(|s| s.as_str()).collect::<Vec<&str>>().join("::")
}
/// Creates a String version of the path to an item,
/// used in navigation between pages.
/// used in navigation between pages. The location given is the break point.
///
/// This is only used for full path syntax, e.g `module/submodule/file_name.html`.
pub(crate) fn to_file_path_string(&self, file_name: &str, location: &str) -> Result<String> {
pub(crate) fn file_path_at_location(&self, file_name: &str, location: &str) -> Result<String> {
let mut iter = self.module_prefixes.iter();
for prefix in iter.by_ref() {
if prefix == location {
Expand All @@ -231,6 +234,44 @@ impl ModuleInfo {
.map(|file_path_str| file_path_str.to_string())
.ok_or_else(|| anyhow::anyhow!("There will always be at least the item name"))
}
/// Compares the current `module_info` to the next `module_info` to determine how many directories to go back to make
/// the next file path valid, and returns that path as a `String`.
///
/// Example:
/// ```
/// // number of dirs: [match][ 2 ][ 1 ]
/// current_location = "project_root/module/submodule1/submodule2/struct.Name.html";
/// next_location = "module/other_submodule/enum.Name.html";
/// result = "../../other_submodule/enum.Name.html";
/// ```
/// In this case the first module to match is "module", so we have no need to go back further than that.
pub(crate) fn file_path_from_location(
&self,
file_name: &str,
current_module_info: &ModuleInfo,
) -> Result<String> {
let mut mid = 0; // the index to split the module_info from call_path at
let mut offset = 0; // the number of directories to go back
let mut next_location_iter = self.module_prefixes.iter().rev().enumerate().peekable();
while let Some((index, prefix)) = next_location_iter.peek() {
for (count, module) in current_module_info.module_prefixes.iter().rev().enumerate() {
if module == *prefix {
offset = count;
mid = self.module_prefixes.len() - index;
break;
}
}
next_location_iter.next();
}
let mut new_path = (0..offset).map(|_| "../").collect::<String>();
write!(
new_path,
"{}/{}",
self.module_prefixes.split_at(mid).1.join("/"),
file_name
)?;
Ok(new_path)
}
/// Create a path `&str` for navigation from the `module.depth()` & `file_name`.
///
/// This is only used for shorthand path syntax, e.g `../../file_name.html`.
Expand All @@ -245,13 +286,25 @@ impl ModuleInfo {
pub(crate) fn depth(&self) -> usize {
self.module_prefixes.len()
}
/// Create a new [ModuleInfo] from a vec.
/// Create a new [ModuleInfo] from a `TyModule`.
pub(crate) fn from_ty_module(module_prefixes: Vec<String>, attributes: Option<String>) -> Self {
Self {
module_prefixes,
attributes,
}
}
/// Create a new [ModuleInfo] from a `CallPath`.
pub(crate) fn from_call_path(call_path: CallPath) -> Self {
let module_prefixes = call_path
.prefixes
.iter()
.map(|p| p.as_str().to_string())
.collect::<Vec<String>>();
Self {
module_prefixes,
attributes: None,
}
}
pub(crate) fn preview_opt(&self) -> Option<String> {
create_preview(self.attributes.clone())
}
Expand Down
35 changes: 33 additions & 2 deletions forc-plugins/forc-doc/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,32 @@ use include_dir::{include_dir, Dir};
use pkg::manifest::ManifestFile;
use std::{
process::Command as Process,
sync::Arc,
{fs, path::PathBuf},
};
use sway_core::{decl_engine::DeclEngine, BuildTarget, Engines, TypeEngine};

/// Information passed to the render phase to get TypeInfo, CallPath or visibility for type anchors.
#[derive(Clone)]
struct RenderPlan {
document_private_items: bool,
type_engine: Arc<TypeEngine>,
decl_engine: Arc<DeclEngine>,
}
impl RenderPlan {
fn new(
document_private_items: bool,
type_engine: Arc<TypeEngine>,
decl_engine: Arc<DeclEngine>,
) -> RenderPlan {
Self {
document_private_items,
type_engine,
decl_engine,
}
}
}

/// Main method for `forc doc`.
pub fn main() -> Result<()> {
let Command {
Expand Down Expand Up @@ -91,8 +113,17 @@ pub fn main() -> Result<()> {
.forc_version
.as_ref()
.map(|ver| format!("{}.{}.{}", ver.major, ver.minor, ver.patch));
let rendered_docs =
RenderedDocumentation::from(raw_docs, root_attributes, program_kind, forc_version)?;
let rendered_docs = RenderedDocumentation::from(
raw_docs,
RenderPlan::new(
document_private_items,
Arc::from(type_engine),
Arc::from(decl_engine),
),
root_attributes,
program_kind,
forc_version,
)?;

// write contents to outfile
for doc in rendered_docs.0 {
Expand Down
Loading

0 comments on commit 276ef41

Please sign in to comment.