Skip to content

Commit

Permalink
Add index file navigation to forc doc (FuelLabs#3778)
Browse files Browse the repository at this point in the history
Closes FuelLabs#3658 
Closes FuelLabs#3170 
Closes FuelLabs#3660 

Updated video using `std`:


https://user-images.githubusercontent.com/57543709/213319718-c8dd9d40-a29b-4598-8db5-89c8d6a5a837.mov

Here's what the files look like, the only difference to rust is that
they create a folder for the parent module as where we omit it. This
will likely need to change once we have support for workspaces.
![Screen Shot 2023-01-17 at 7 51 32
PM](https://user-images.githubusercontent.com/57543709/213061699-12ac1b3a-3e2e-4469-8f63-737549564b91.png)

Co-authored-by: Joshua Batty <[email protected]>
  • Loading branch information
eureka-cpu and JoshuaBatty authored Jan 19, 2023
1 parent 0c99c8a commit 6319f8e
Show file tree
Hide file tree
Showing 4 changed files with 763 additions and 188 deletions.
97 changes: 78 additions & 19 deletions forc-plugins/forc-doc/src/doc.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
descriptor::Descriptor,
render::{ItemBody, ItemHeader, Renderable},
render::{DocLink, ItemBody, ItemHeader, Renderable},
};
use anyhow::Result;
use horrorshow::{box_html, RenderBox};
Expand All @@ -21,23 +21,31 @@ pub(crate) struct Document {
}
impl Document {
/// Creates an HTML file name from the [Document].
pub(crate) fn html_file_name(&self) -> String {
pub(crate) fn html_filename(&self) -> String {
use sway_core::language::ty::TyDeclaration::StorageDeclaration;
let name = match &self.item_body.ty_decl {
StorageDeclaration(_) => None,
_ => Some(self.item_header.item_name.as_str()),
};

Document::create_html_file_name(self.item_body.ty_decl.doc_name(), name)
Document::create_html_filename(self.item_body.ty_decl.doc_name(), name)
}
fn create_html_file_name<'name>(ty: &'name str, name: Option<&'name str>) -> String {
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(),
}
}
/// Gather [Documentation] from the [TyProgram].
pub(crate) fn from_ty_program(
decl_engine: &DeclEngine,
Expand Down Expand Up @@ -127,7 +135,7 @@ impl Renderable for Document {
}
}
pub(crate) type ModulePrefix = String;
#[derive(Clone)]
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq)]
pub(crate) struct ModuleInfo(pub(crate) Vec<ModulePrefix>);
impl ModuleInfo {
/// The current module.
Expand All @@ -136,36 +144,64 @@ impl ModuleInfo {
.last()
.expect("There will always be at least the project name")
}
/// The location of the parent of the current module.
///
/// To be used in path navigation between modules.
pub(crate) fn _parent(&mut self) -> &str {
self.0.pop();
self.location()
}
/// The name of the project.
pub(crate) fn project_name(&self) -> &str {
self.0.first().expect("Project name missing")
}
/// The location of the parent of the current module.
///
/// Returns `None` if there is no parent.
pub(crate) fn parent(&self) -> Option<&String> {
match self.has_parent() {
true => {
let mut iter = self.0.iter();
iter.next_back();
iter.next_back()
}
false => None,
}
}
/// Determines if the current module has a parent module.
fn has_parent(&self) -> bool {
self.depth() > 1
}
pub(crate) fn is_root_module(&self) -> bool {
self.location() == self.project_name()
}
/// Create a qualified path literal String that represents the full path to an item.
pub(crate) fn to_path_literal_str(&self, item_name: &str) -> String {
let prefix = self.to_path_literal_prefix();
///
/// Example: `project_name::module::Item`
pub(crate) fn to_path_literal_string(&self, item_name: &str, location: &str) -> String {
let prefix = self.to_path_literal_prefix(location);
match prefix.is_empty() {
true => item_name.to_owned(),
false => format!("{}::{}", prefix, item_name),
}
}
/// Create a path literal prefix from the module prefixes.
fn to_path_literal_prefix(&self) -> String {
/// Use in `to_path_literal_string()` to create a full literal path string.
///
/// Example: `module::submodule`
fn to_path_literal_prefix(&self, location: &str) -> String {
let mut iter = self.0.iter();
iter.next(); // skip the project name
for prefix in iter.by_ref() {
if prefix == location {
break;
}
}
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.
pub(crate) fn to_file_path_str(&self, file_name: &str) -> String {
///
/// 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) -> String {
let mut iter = self.0.iter();
iter.next(); // skip the project_name
for prefix in iter.by_ref() {
if prefix == location {
break;
}
}
let mut file_path = iter.collect::<PathBuf>();
file_path.push(file_name);

Expand All @@ -175,7 +211,9 @@ impl ModuleInfo {
.to_string()
}
/// Create a path `&str` for navigation from the `module.depth()` & `file_name`.
pub(crate) fn to_html_shorthand_path_str(&self, file_name: &str) -> String {
///
/// This is only used for shorthand path syntax, e.g `../../file_name.html`.
pub(crate) fn to_html_shorthand_path_string(&self, file_name: &str) -> String {
format!("{}{}", self.to_html_path_prefix(), file_name)
}
/// Create a path prefix `&str` for navigation from the `module.depth()`.
Expand All @@ -191,3 +229,24 @@ impl ModuleInfo {
Self(vec)
}
}

#[cfg(test)]
mod tests {
use super::ModuleInfo;

#[test]
fn test_parent() {
let project = String::from("project_name");
let module = String::from("module_name");
let mut module_vec = vec![project.clone(), module];

let module_info = ModuleInfo::from_vec(module_vec.clone());
let project_opt = module_info.parent();
assert_eq!(Some(&project), project_opt);

module_vec.pop();
let module_info = ModuleInfo::from_vec(module_vec);
let project_opt = module_info.parent();
assert_eq!(None, project_opt);
}
}
13 changes: 9 additions & 4 deletions forc-plugins/forc-doc/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ mod render;

use crate::{
doc::{Document, Documentation},
render::{RenderedDocumentation, ALL_DOC_FILENAME},
render::{RenderedDocumentation, INDEX_FILENAME},
};
use anyhow::{bail, Result};
use clap::Parser;
Expand Down Expand Up @@ -83,7 +83,12 @@ pub fn main() -> Result<()> {
document_private_items,
)?;
// render docs to HTML
let rendered_docs = RenderedDocumentation::from(raw_docs);
let forc_version = pkg_manifest
.project
.forc_version
.as_ref()
.map(|ver| format!("{}.{}.{}", ver.major, ver.minor, ver.patch));
let rendered_docs = RenderedDocumentation::from(raw_docs, forc_version);

// write contents to outfile
for doc in rendered_docs.0 {
Expand All @@ -95,7 +100,7 @@ pub fn main() -> Result<()> {
}

fs::create_dir_all(&doc_path)?;
doc_path.push(doc.html_file_name);
doc_path.push(doc.html_filename);
fs::write(&doc_path, doc.file_contents.0.as_bytes())?;
}
// CSS, icons and logos
Expand All @@ -116,7 +121,7 @@ pub fn main() -> Result<()> {
// if opening in the browser fails, attempt to open using a file explorer
if open_result {
const BROWSER_ENV_VAR: &str = "BROWSER";
let path = doc_path.join(ALL_DOC_FILENAME);
let path = doc_path.join(INDEX_FILENAME);
let default_browser_opt = std::env::var_os(BROWSER_ENV_VAR);
match default_browser_opt {
Some(def_browser) => {
Expand Down
Loading

0 comments on commit 6319f8e

Please sign in to comment.