From 203b59053c713574007a94663923411f18c501ef Mon Sep 17 00:00:00 2001 From: Chris O'Brien <57543709+eureka-cpu@users.noreply.github.com> Date: Tue, 25 Oct 2022 18:58:24 -0500 Subject: [PATCH] Implement `forc doc` (#2869) Opening up for visibility, still reading through `cargo doc` and previous comments related to this Closes #161 - [x] #3027 - [x] #3033 - [x] #3036 - [x] #3038 - [x] #3088 - [x] #3090 Co-authored-by: Alex Hansen Co-authored-by: Alex --- Cargo.lock | 20 + Cargo.toml | 1 + examples/structs/Forc.lock | 1 - forc-plugins/forc-doc/Cargo.toml | 20 + forc-plugins/forc-doc/src/cli.rs | 26 + forc-plugins/forc-doc/src/descriptor.rs | 118 ++++ forc-plugins/forc-doc/src/doc.rs | 110 ++++ forc-plugins/forc-doc/src/main.rs | 91 +++ forc-plugins/forc-doc/src/render.rs | 522 ++++++++++++++++++ .../declaration_engine/declaration_engine.rs | 6 +- .../declaration_engine/declaration_wrapper.rs | 2 +- .../src/language/parsed/declaration/trait.rs | 4 +- sway-core/src/language/ty/declaration/abi.rs | 4 +- .../src/language/ty/declaration/constant.rs | 2 +- sway-core/src/language/ty/declaration/enum.rs | 4 +- .../src/language/ty/declaration/function.rs | 6 +- .../src/language/ty/declaration/impl_trait.rs | 2 +- .../src/language/ty/declaration/struct.rs | 4 +- .../src/language/ty/declaration/trait.rs | 4 +- sway-core/src/semantic_analysis.rs | 3 +- 20 files changed, 928 insertions(+), 22 deletions(-) create mode 100644 forc-plugins/forc-doc/Cargo.toml create mode 100644 forc-plugins/forc-doc/src/cli.rs create mode 100644 forc-plugins/forc-doc/src/descriptor.rs create mode 100644 forc-plugins/forc-doc/src/doc.rs create mode 100644 forc-plugins/forc-doc/src/main.rs create mode 100644 forc-plugins/forc-doc/src/render.rs diff --git a/Cargo.lock b/Cargo.lock index 71bdc152847..9316824e811 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1587,6 +1587,20 @@ dependencies = [ "tracing", ] +[[package]] +name = "forc-doc" +version = "0.28.1" +dependencies = [ + "anyhow", + "clap 4.0.18", + "forc-pkg", + "forc-util", + "horrorshow", + "opener", + "sway-core", + "sway-types", +] + [[package]] name = "forc-explore" version = "0.28.1" @@ -2335,6 +2349,12 @@ dependencies = [ "digest 0.10.5", ] +[[package]] +name = "horrorshow" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8371fb981840150b1a54f7cb117bf6699f7466a1d4861daac33bc6fe2b5abea0" + [[package]] name = "http" version = "0.2.8" diff --git a/Cargo.toml b/Cargo.toml index 9fe5d827a7c..47dae545d58 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ members = [ "forc-plugins/forc-explore", "forc-plugins/forc-fmt", "forc-plugins/forc-lsp", + "forc-plugins/forc-doc", "forc-tracing", "forc-util", "scripts/examples-checker", diff --git a/examples/structs/Forc.lock b/examples/structs/Forc.lock index 55d92084ada..f485821d7ed 100644 --- a/examples/structs/Forc.lock +++ b/examples/structs/Forc.lock @@ -1,4 +1,3 @@ [[package]] name = 'structs' source = 'root' -dependencies = [] diff --git a/forc-plugins/forc-doc/Cargo.toml b/forc-plugins/forc-doc/Cargo.toml new file mode 100644 index 00000000000..7bd4f3fb2be --- /dev/null +++ b/forc-plugins/forc-doc/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "forc-doc" +version = "0.28.1" +authors = ["Fuel Labs "] +edition = "2021" +homepage = "https://fuel.network/" +license = "Apache-2.0" +repository = "https://github.com/FuelLabs/sway" +description = "Build the documentation for the local package and all dependencies. The output is placed in `out/doc` in the same format as the project." + +[dependencies] +anyhow = "1.0.65" +clap = { version = "4.0.18", features = ["derive"] } +forc-pkg = { version = "0.28.1", path = "../../forc-pkg" } +forc-util = { version = "0.28.1", path = "../../forc-util"} +horrorshow = "0.8.4" +opener = "0.5.0" +sway-core = { version = "0.28.1", path = "../../sway-core" } +sway-types = { version = "0.28.1", path = "../../sway-types" } + diff --git a/forc-plugins/forc-doc/src/cli.rs b/forc-plugins/forc-doc/src/cli.rs new file mode 100644 index 00000000000..2a1476f2163 --- /dev/null +++ b/forc-plugins/forc-doc/src/cli.rs @@ -0,0 +1,26 @@ +use clap::Parser; + +#[derive(Debug, Parser)] +pub struct Command { + /// Path to the Forc.toml file. By default, Cargo searches for the Forc.toml + /// file in the current directory or any parent directory. + #[clap(long)] + pub manifest_path: Option, + /// Open the docs in a browser after building them. + #[clap(long)] + pub open: bool, + /// Offline mode, prevents Forc from using the network when managing dependencies. + /// Meaning it will only try to use previously downloaded dependencies. + #[clap(long = "offline")] + pub offline: bool, + /// Silent mode. Don't output any warnings or errors to the command line. + #[clap(long = "silent", short = 's')] + pub silent: bool, + /// Requires that the Forc.lock file is up-to-date. If the lock file is missing, or it + /// needs to be updated, Forc will exit with an error + #[clap(long)] + pub locked: bool, + /// Do not build documentation for dependencies. + #[clap(long)] + pub no_deps: bool, +} diff --git a/forc-plugins/forc-doc/src/descriptor.rs b/forc-plugins/forc-doc/src/descriptor.rs new file mode 100644 index 00000000000..da50ff26c9c --- /dev/null +++ b/forc-plugins/forc-doc/src/descriptor.rs @@ -0,0 +1,118 @@ +//! Determine whether a [Declaration] is documentable. +use anyhow::Result; +use sway_core::{ + declaration_engine::*, + language::ty::{ + TyAbiDeclaration, TyConstantDeclaration, TyDeclaration, TyEnumDeclaration, + TyFunctionDeclaration, TyImplTrait, TyStorageDeclaration, TyStructDeclaration, + TyTraitDeclaration, + }, +}; +use sway_types::Spanned; + +#[derive(Eq, PartialEq, Debug)] +// TODO: See if there's a way we can use the TyDeclarations directly +// +/// The type of [TyDeclaration] documented by the [Descriptor]. +pub(crate) enum DescriptorType { + Struct(TyStructDeclaration), + Enum(TyEnumDeclaration), + Trait(TyTraitDeclaration), + Abi(TyAbiDeclaration), + Storage(TyStorageDeclaration), + ImplTraitDesc(TyImplTrait), + Function(TyFunctionDeclaration), + Const(Box), +} +impl DescriptorType { + /// Converts the [DescriptorType] to a `&str` name for HTML file name creation. + pub fn as_str(&self) -> &'static str { + use DescriptorType::*; + match self { + Struct(_) => "struct", + Enum(_) => "enum", + Trait(_) => "trait", + Abi(_) => "abi", + Storage(_) => "storage", + ImplTraitDesc(_) => "impl_trait", + Function(_) => "function", + Const(_) => "const", + } + } +} + +#[derive(Eq, PartialEq)] +/// Used in deciding whether or not a [Declaration] is documentable. +pub(crate) enum Descriptor { + Documentable { + // If empty, this is the root. + module_prefix: Vec, + // We want _all_ of the TyDeclaration information. + desc_ty: Box, + }, + NonDocumentable, +} +impl Descriptor { + pub(crate) fn from_typed_decl(d: &TyDeclaration, module_prefix: Vec) -> Result { + use TyDeclaration::*; + match d { + StructDeclaration(ref decl_id) => { + let struct_decl = de_get_struct(decl_id.clone(), &decl_id.span())?; + Ok(Descriptor::Documentable { + module_prefix, + desc_ty: Box::new(DescriptorType::Struct(struct_decl)), + }) + } + EnumDeclaration(ref decl_id) => { + let enum_decl = de_get_enum(decl_id.clone(), &decl_id.span())?; + Ok(Descriptor::Documentable { + module_prefix, + desc_ty: Box::new(DescriptorType::Enum(enum_decl)), + }) + } + TraitDeclaration(ref decl_id) => { + let trait_decl = de_get_trait(decl_id.clone(), &decl_id.span())?; + Ok(Descriptor::Documentable { + module_prefix, + desc_ty: Box::new(DescriptorType::Trait(trait_decl)), + }) + } + AbiDeclaration(ref decl_id) => { + let abi_decl = de_get_abi(decl_id.clone(), &decl_id.span())?; + Ok(Descriptor::Documentable { + module_prefix, + desc_ty: Box::new(DescriptorType::Abi(abi_decl)), + }) + } + StorageDeclaration(ref decl_id) => { + let storage_decl = de_get_storage(decl_id.clone(), &decl_id.span())?; + Ok(Descriptor::Documentable { + module_prefix, + desc_ty: Box::new(DescriptorType::Storage(storage_decl)), + }) + } + ImplTrait(ref decl_id) => { + let impl_trait = de_get_impl_trait(decl_id.clone(), &decl_id.span())?; + Ok(Descriptor::Documentable { + module_prefix, + desc_ty: Box::new(DescriptorType::ImplTraitDesc(impl_trait)), + }) + } + FunctionDeclaration(ref decl_id) => { + let fn_decl = de_get_function(decl_id.clone(), &decl_id.span())?; + Ok(Descriptor::Documentable { + module_prefix, + desc_ty: Box::new(DescriptorType::Function(fn_decl)), + }) + } + ConstantDeclaration(ref decl_id) => { + let const_decl = de_get_constant(decl_id.clone(), &decl_id.span())?; + Ok(Descriptor::Documentable { + module_prefix, + desc_ty: Box::new(DescriptorType::Const(Box::new(const_decl))), + }) + } + _ => Ok(Descriptor::NonDocumentable), + } + } +} diff --git a/forc-plugins/forc-doc/src/doc.rs b/forc-plugins/forc-doc/src/doc.rs new file mode 100644 index 00000000000..cd6ac0a4373 --- /dev/null +++ b/forc-plugins/forc-doc/src/doc.rs @@ -0,0 +1,110 @@ +use crate::descriptor::{Descriptor, DescriptorType}; +use anyhow::Result; +use sway_core::{ + language::{ + ty::{TyAstNodeContent, TySubmodule}, + {parsed::ParseProgram, ty::TyProgram}, + }, + CompileResult, +}; + +pub(crate) type Documentation = Vec; +/// A finalized Document ready to be rendered. We want to retain all +/// information including spans, fields on structs, variants on enums etc. +pub(crate) struct Document { + pub(crate) module_prefix: Vec, + pub(crate) desc_ty: DescriptorType, +} +impl Document { + // Creates an HTML file name from the [Document]. + pub fn file_name(&self) -> String { + use DescriptorType::*; + let name = match &self.desc_ty { + Struct(ty_struct_decl) => Some(ty_struct_decl.name.as_str()), + Enum(ty_enum_decl) => Some(ty_enum_decl.name.as_str()), + Trait(ty_trait_decl) => Some(ty_trait_decl.name.as_str()), + Abi(ty_abi_decl) => Some(ty_abi_decl.name.as_str()), + Storage(_) => None, // storage does not have an Ident + ImplTraitDesc(ty_impl_trait) => Some(ty_impl_trait.trait_name.suffix.as_str()), // TODO: check validity + Function(ty_fn_decl) => Some(ty_fn_decl.name.as_str()), + Const(ty_const_decl) => Some(ty_const_decl.name.as_str()), + }; + + Document::create_html_file_name(self.desc_ty.as_str(), name) + } + fn create_html_file_name(ty: &str, name: Option<&str>) -> String { + match name { + Some(name) => { + format!("{ty}.{name}.html") + } + None => { + format!("{ty}.html") // storage does not have an Ident + } + } + } + /// Gather [Documentation] from the [CompileResult]. + pub(crate) fn from_ty_program( + compilation: &CompileResult<(ParseProgram, Option)>, + no_deps: bool, + ) -> Result { + let mut docs: Documentation = Default::default(); + if let Some((_, Some(typed_program))) = &compilation.value { + 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, vec![])?; + + if let Descriptor::Documentable { + module_prefix, + desc_ty, + } = desc + { + docs.push(Document { + module_prefix, + desc_ty: *desc_ty, + }) + } + } + } + + 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 module_prefix = vec![]; + Document::from_ty_submodule(typed_submodule, &mut docs, &module_prefix)?; + } + } + } + + Ok(docs) + } + fn from_ty_submodule( + typed_submodule: &TySubmodule, + docs: &mut Documentation, + module_prefix: &[String], + ) -> Result<()> { + let mut new_submodule_prefix = module_prefix.to_owned(); + new_submodule_prefix.push(typed_submodule.library_name.as_str().to_string()); + 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, new_submodule_prefix.clone())?; + + if let Descriptor::Documentable { + module_prefix, + desc_ty, + } = desc + { + docs.push(Document { + module_prefix, + desc_ty: *desc_ty, + }) + } + } + } + // if there is another submodule we need to go a level deeper + if let Some((_, submodule)) = typed_submodule.module.submodules.first() { + Document::from_ty_submodule(submodule, docs, &new_submodule_prefix)?; + } + + Ok(()) + } +} diff --git a/forc-plugins/forc-doc/src/main.rs b/forc-plugins/forc-doc/src/main.rs new file mode 100644 index 00000000000..13642c76099 --- /dev/null +++ b/forc-plugins/forc-doc/src/main.rs @@ -0,0 +1,91 @@ +mod cli; +mod descriptor; +mod doc; +mod render; + +use anyhow::{bail, Result}; +use clap::Parser; +use cli::Command; +use std::{ + process::Command as Process, + {fs, path::PathBuf}, +}; + +use crate::{ + doc::{Document, Documentation}, + render::{RenderedDocument, RenderedDocumentation}, +}; +use forc_pkg::{self as pkg, PackageManifestFile}; +use forc_util::default_output_directory; + +/// Main method for `forc doc`. +pub fn main() -> Result<()> { + let Command { + manifest_path, + open: open_result, + offline, + silent, + locked, + no_deps, + } = Command::parse(); + + // get manifest directory + let dir = if let Some(ref path) = manifest_path { + PathBuf::from(path) + } else { + std::env::current_dir()? + }; + let manifest = PackageManifestFile::from_dir(&dir)?; + + // check if the out path exists + let project_name = &manifest.project.name; + let out_path = default_output_directory(manifest.dir()); + let doc_path = out_path.join("doc"); + fs::create_dir_all(&doc_path)?; + + // compile the program and extract the docs + let plan = pkg::BuildPlan::from_lock_and_manifest(&manifest, locked, offline)?; + let compilation = pkg::check(&plan, silent)?; + let raw_docs: Documentation = Document::from_ty_program(&compilation, no_deps)?; + // render docs to HTML + let rendered_docs: RenderedDocumentation = + RenderedDocument::from_raw_docs(&raw_docs, project_name); + + // write to outfile + for doc in rendered_docs { + let mut doc_path = doc_path.clone(); + for prefix in doc.module_prefix { + doc_path.push(prefix); + } + + fs::create_dir_all(&doc_path)?; + doc_path.push(doc.file_name); + fs::write(&doc_path, doc.file_contents.0.as_bytes())?; + } + + // check if the user wants to open the doc in the browser + // if opening in the browser fails, attempt to open using a file explorer + if open_result { + let path = doc_path.join("all.html"); + let default_browser_opt = std::env::var_os("BROWSER"); + match default_browser_opt { + Some(def_browser) => { + let browser = PathBuf::from(def_browser); + if let Err(e) = Process::new(&browser).arg(path).status() { + bail!( + "Couldn't open docs with {}: {}", + browser.to_string_lossy(), + e + ); + } + } + None => { + if let Err(e) = opener::open(&path) { + bail!("Couldn't open docs: {}", e); + } + } + } + } + + Ok(()) +} diff --git a/forc-plugins/forc-doc/src/render.rs b/forc-plugins/forc-doc/src/render.rs new file mode 100644 index 00000000000..cba981f0d99 --- /dev/null +++ b/forc-plugins/forc-doc/src/render.rs @@ -0,0 +1,522 @@ +use std::path::PathBuf; + +use crate::{descriptor::DescriptorType, doc::Documentation}; +use horrorshow::{box_html, helper::doctype, html, prelude::*}; +use sway_core::language::ty::{ + TyAbiDeclaration, TyConstantDeclaration, TyEnumDeclaration, TyFunctionDeclaration, TyImplTrait, + TyStorageDeclaration, TyStructDeclaration, TyTraitDeclaration, +}; +use sway_core::transform::{AttributeKind, AttributesMap}; + +pub(crate) struct HTMLString(pub(crate) String); +pub(crate) type RenderedDocumentation = Vec; +enum ItemType { + Struct, + Enum, + Trait, + Abi, + Storage, + Function, + Constant, +} +type AllDoc = Vec<(ItemType, (String, Vec, String))>; +/// A [Document] rendered to HTML. +pub(crate) struct RenderedDocument { + pub(crate) module_prefix: Vec, + pub(crate) file_name: String, + pub(crate) file_contents: HTMLString, +} +impl RenderedDocument { + /// Top level HTML rendering for all [Documentation] of a program. + pub fn from_raw_docs(raw: &Documentation, project_name: &String) -> RenderedDocumentation { + let mut rendered_docs: RenderedDocumentation = Default::default(); + let mut all_doc: AllDoc = Default::default(); + for doc in raw { + let module_prefix = doc.module_prefix.clone(); + let module_depth = module_prefix.len(); + let module = if module_prefix.last().is_some() { + module_prefix.last().unwrap().to_string() + } else { + project_name.to_string() + }; + let file_name = doc.file_name(); + let decl_ty = doc.desc_ty.as_str().to_string(); + let rendered_content = match &doc.desc_ty { + DescriptorType::Struct(struct_decl) => { + let path_str = if module_depth == 0 { + struct_decl.name.as_str().to_string() + } else { + format!("{}::{}", &module, &struct_decl.name) + }; + all_doc.push(( + ItemType::Struct, + (path_str, module_prefix.clone(), file_name.clone()), + )); + struct_decl.render(module, module_depth, decl_ty) + } + DescriptorType::Enum(enum_decl) => { + let path_str = if module_depth == 0 { + enum_decl.name.as_str().to_string() + } else { + format!("{}::{}", &module, &enum_decl.name) + }; + all_doc.push(( + ItemType::Enum, + (path_str, module_prefix.clone(), file_name.clone()), + )); + enum_decl.render(module, module_depth, decl_ty) + } + DescriptorType::Trait(trait_decl) => { + let path_str = if module_depth == 0 { + trait_decl.name.as_str().to_string() + } else { + format!("{}::{}", &module, &trait_decl.name) + }; + all_doc.push(( + ItemType::Trait, + (path_str, module_prefix.clone(), file_name.clone()), + )); + trait_decl.render(module, module_depth, decl_ty) + } + DescriptorType::Abi(abi_decl) => { + let path_str = if module_depth == 0 { + abi_decl.name.as_str().to_string() + } else { + format!("{}::{}", &module, &abi_decl.name) + }; + all_doc.push(( + ItemType::Abi, + (path_str, module_prefix.clone(), file_name.clone()), + )); + abi_decl.render(module, module_depth, decl_ty) + } + DescriptorType::Storage(storage_decl) => { + all_doc.push(( + ItemType::Storage, + ( + format!("{}::ContractStorage", &module), + module_prefix.clone(), + file_name.clone(), + ), + )); + storage_decl.render(module, module_depth, decl_ty) + } + // TODO: Figure out how to represent impl traits + DescriptorType::ImplTraitDesc(impl_trait_decl) => { + impl_trait_decl.render(module, module_depth, decl_ty) + } + DescriptorType::Function(fn_decl) => { + let path_str = if module_depth == 0 { + fn_decl.name.as_str().to_string() + } else { + format!("{}::{}", &module, &fn_decl.name) + }; + all_doc.push(( + ItemType::Function, + (path_str, module_prefix.clone(), file_name.clone()), + )); + fn_decl.render(module, module_depth, decl_ty) + } + DescriptorType::Const(const_decl) => { + let path_str = if module_depth == 0 { + const_decl.name.as_str().to_string() + } else { + format!("{}::{}", &module, &const_decl.name) + }; + all_doc.push(( + ItemType::Constant, + (path_str, module_prefix.clone(), file_name.clone()), + )); + const_decl.render(module, module_depth, decl_ty) + } + }; + rendered_docs.push(Self { + module_prefix, + file_name, + file_contents: HTMLString(page_from(rendered_content)), + }) + } + // All Doc + rendered_docs.push(Self { + module_prefix: vec![], + file_name: "all.html".to_string(), + file_contents: HTMLString(page_from(all_items(project_name.to_string(), &all_doc))), + }); + rendered_docs + } +} + +fn page_from(rendered_content: Box) -> String { + let markup = html! { + : doctype::HTML; + html { + : rendered_content + } + }; + + markup.into_string().unwrap() +} + +/// Basic HTML header component +fn html_head(location: String, decl_ty: String, decl_name: String) -> Box { + box_html! { + head { + meta(charset="utf-8"); + meta(name="viewport", content="width=device-width, initial-scale=1.0"); + meta(name="generator", content="forcdoc"); + meta( + name="description", + content=format!("API documentation for the Sway `{decl_name}` {decl_ty} in `{location}`.") + ); + meta(name="keywords", content=format!("sway, swaylang, sway-lang, {decl_name}")); + title: format!("{decl_name} in {location} - Sway"); + // TODO: Add links for CSS & Fonts + } + } +} +/// HTML body component +fn html_body( + module_depth: usize, + decl_ty: String, + decl_name: String, + code_span: String, + item_attrs: String, +) -> Box { + let href = if module_depth > 0 { + "../all.html" + } else { + "../doc/all.html" + } + .to_string(); + + box_html! { + body(class=format!("forcdoc {decl_ty}")) { + : sidebar(decl_name, href); + // create main + // create main content + + // this is the main code block + div(class="docblock item-decl") { + pre(class=format!("sway {decl_ty}")) { + code { : code_span; } + } + } + // expand or hide description of main code block + details(class="forcdoc-toggle top-doc", open) { + summary(class="hideme") { + span { : "Expand description" } + } + // this is the description + div(class="docblock") { + p { : item_attrs; } + } + } + } + } +} +// crate level index.html +fn _crate_index() -> Box { + box_html! {} +} +// crate level, all items belonging to a crate +fn all_items(crate_name: String, all_doc: &AllDoc) -> Box { + // TODO: find a better way to do this + // + // we need to have a finalized list for the all doc + let mut struct_items: Vec<(String, String)> = Vec::new(); + let mut enum_items: Vec<(String, String)> = Vec::new(); + let mut trait_items: Vec<(String, String)> = Vec::new(); + let mut abi_items: Vec<(String, String)> = Vec::new(); + let mut storage_items: Vec<(String, String)> = Vec::new(); + let mut fn_items: Vec<(String, String)> = Vec::new(); + let mut const_items: Vec<(String, String)> = Vec::new(); + for (ty, (path_str, module_prefix, file_name)) in all_doc { + match ty { + ItemType::Struct => struct_items.push(( + path_str.clone(), + qualified_file_path(module_prefix, file_name.clone()), + )), + ItemType::Enum => enum_items.push(( + path_str.clone(), + qualified_file_path(module_prefix, file_name.clone()), + )), + ItemType::Trait => trait_items.push(( + path_str.clone(), + qualified_file_path(module_prefix, file_name.clone()), + )), + ItemType::Abi => abi_items.push(( + path_str.clone(), + qualified_file_path(module_prefix, file_name.clone()), + )), + ItemType::Storage => storage_items.push(( + path_str.clone(), + qualified_file_path(module_prefix, file_name.clone()), + )), + ItemType::Function => fn_items.push(( + path_str.clone(), + qualified_file_path(module_prefix, file_name.clone()), + )), + ItemType::Constant => const_items.push(( + path_str.clone(), + qualified_file_path(module_prefix, file_name.clone()), + )), + } + } + box_html! { + head { + meta(charset="utf-8"); + meta(name="viewport", content="width=device-width, initial-scale=1.0"); + meta(name="generator", content="forcdoc"); + meta( + name="description", + content="List of all items in this crate" + ); + meta(name="keywords", content="sway, swaylang, sway-lang"); + title: "List of all items in this crate"; + } + body(class="forcdoc mod") { + : sidebar(format!("Crate {crate_name}"), "../doc/all.html".to_string()); + section(id="main-content", class="content") { + h1(class="fqn") { + span(class="in-band") { : "List of all items" } + } + @ if !storage_items.is_empty() { + : all_items_list("Contract Storage".to_string(), storage_items); + } + @ if !abi_items.is_empty() { + : all_items_list("Abi".to_string(), abi_items); + } + @ if !trait_items.is_empty() { + : all_items_list("Traits".to_string(), trait_items); + } + @ if !struct_items.is_empty() { + : all_items_list("Structs".to_string(), struct_items); + } + @ if !enum_items.is_empty() { + : all_items_list("Enums".to_string(), enum_items); + } + @ if !fn_items.is_empty() { + : all_items_list("Functions".to_string(), fn_items); + } + @ if !const_items.is_empty() { + : all_items_list("Constants".to_string(), const_items); + } + } + } + } +} +fn all_items_list(title: String, list_items: Vec<(String, String)>) -> Box { + box_html! { + h3(id=format!("{title}")) { : title.clone(); } + ul(class=format!("{} docblock", title.to_lowercase())) { + @ for (path_str, file_name) in list_items { + li { + a(href=file_name) { : path_str; } + } + } + } + } +} +// module level index.html +// for each module we need to create an index +// that will have all of the item docs in it +fn _module_index() -> Box { + box_html! {} +} +fn sidebar( + location: String, + href: String, /* sidebar_items: Option>, */ +) -> Box { + box_html! { + nav(class="sidebar") { + a(class="sidebar-logo", href=href) { + div(class="logo-container") { + img(class="sway-logo", src="../sway-logo.svg", alt="logo"); + } + } + h2(class="location") { : location; } + } + } +} +fn qualified_file_path(module_prefix: &Vec, file_name: String) -> String { + let mut file_path = PathBuf::new(); + for prefix in module_prefix { + file_path.push(prefix) + } + file_path.push(file_name); + + file_path.to_str().unwrap().to_string() +} + +fn doc_attributes_to_string_vec(attributes: &AttributesMap) -> String { + let attributes = attributes.get(&AttributeKind::Doc); + let mut attr_strings = String::new(); + if let Some(vec_attrs) = attributes { + for attribute in vec_attrs { + for ident in &attribute.args { + attr_strings.push_str(ident.as_str()) + } + } + } + + attr_strings +} +trait Renderable { + fn render(&self, module: String, module_depth: usize, decl_ty: String) -> Box; +} + +impl Renderable for TyStructDeclaration { + fn render(&self, module: String, module_depth: usize, decl_ty: String) -> Box { + let TyStructDeclaration { + name, + fields: _, + type_parameters: _, + visibility: _, + attributes, + span, + } = &self; + let name = name.as_str().to_string(); + let code_span = span.as_str().to_string(); + let struct_attributes = doc_attributes_to_string_vec(attributes); + box_html! { + : html_head(module.clone(), decl_ty.clone(), name.clone()); + : html_body(module_depth,decl_ty.clone(), name.clone(), code_span, struct_attributes); + } + } +} +impl Renderable for TyEnumDeclaration { + fn render(&self, module: String, module_depth: usize, decl_ty: String) -> Box { + let TyEnumDeclaration { + name, + type_parameters: _, + attributes, + variants: _, + visibility: _, + span, + } = &self; + let name = name.as_str().to_string(); + let code_span = span.as_str().to_string(); + let enum_attributes = doc_attributes_to_string_vec(attributes); + box_html! { + : html_head(module.clone(), decl_ty.clone(), name.clone()); + : html_body(module_depth,decl_ty.clone(), name.clone(), code_span, enum_attributes); + } + } +} +impl Renderable for TyTraitDeclaration { + fn render(&self, module: String, module_depth: usize, decl_ty: String) -> Box { + let TyTraitDeclaration { + name, + interface_surface: _, + methods: _, + visibility: _, + attributes, + supertraits: _, + span, + } = &self; + let name = name.as_str().to_string(); + let code_span = span.as_str().to_string(); + let trait_attributes = doc_attributes_to_string_vec(attributes); + box_html! { + : html_head(module.clone(), decl_ty.clone(), name.clone()); + : html_body(module_depth,decl_ty.clone(), name.clone(), code_span, trait_attributes); + } + } +} +impl Renderable for TyAbiDeclaration { + fn render(&self, module: String, module_depth: usize, decl_ty: String) -> Box { + let TyAbiDeclaration { + name, + interface_surface: _, + methods: _, + attributes, + span, + } = &self; + let name = name.as_str().to_string(); + let code_span = span.as_str().to_string(); + let abi_attributes = doc_attributes_to_string_vec(attributes); + box_html! { + : html_head(module.clone(), decl_ty.clone(), name.clone()); + : html_body(module_depth,decl_ty.clone(), name.clone(), code_span, abi_attributes); + } + } +} +impl Renderable for TyStorageDeclaration { + fn render(&self, module: String, module_depth: usize, decl_ty: String) -> Box { + let TyStorageDeclaration { + fields: _, + span, + attributes, + } = &self; + let name = "Contract Storage".to_string(); + let code_span = span.as_str().to_string(); + let storage_attributes = doc_attributes_to_string_vec(attributes); + box_html! { + : html_head(module.clone(), decl_ty.clone(), name.clone()); + : html_body(module_depth,decl_ty.clone(), name.clone(), code_span, storage_attributes); + } + } +} +impl Renderable for TyImplTrait { + fn render(&self, module: String, module_depth: usize, decl_ty: String) -> Box { + let TyImplTrait { + impl_type_parameters: _, + trait_name, + trait_type_parameters: _, + methods: _, + implementing_for_type_id: _, + type_implementing_for_span: _, + span, + } = &self; + let name = trait_name.suffix.as_str().to_string(); + let code_span = span.as_str().to_string(); + // let impl_trait_attributes = doc_attributes_to_string_vec(attributes); + box_html! { + : html_head(module.clone(), decl_ty.clone(), name.clone()); + : html_body(module_depth,decl_ty.clone(), name.clone(), code_span, "".to_string()); + } + } +} +impl Renderable for TyFunctionDeclaration { + fn render(&self, module: String, module_depth: usize, decl_ty: String) -> Box { + let TyFunctionDeclaration { + name, + body: _, + parameters: _, + span, + attributes, + return_type: _, + initial_return_type: _, + type_parameters: _, + return_type_span: _, + purity: _, + is_contract_call: _, + visibility: _, + } = &self; + let name = name.as_str().to_string(); + let code_span = span.as_str().to_string(); + let function_attributes = doc_attributes_to_string_vec(attributes); + box_html! { + : html_head(module.clone(), decl_ty.clone(), name.clone()); + : html_body(module_depth,decl_ty.clone(), name.clone(), code_span, function_attributes); + } + } +} +impl Renderable for TyConstantDeclaration { + fn render(&self, module: String, module_depth: usize, decl_ty: String) -> Box { + let TyConstantDeclaration { + name, + value: _, + attributes, + visibility: _, + span, + } = &self; + let name = name.as_str().to_string(); + let code_span = span.as_str().to_string(); + let const_attributes = doc_attributes_to_string_vec(attributes); + box_html! { + : html_head(module.clone(), decl_ty.clone(), name.clone()); + : html_body(module_depth, decl_ty.clone(), name.clone(), code_span, const_attributes); + } + } +} diff --git a/sway-core/src/declaration_engine/declaration_engine.rs b/sway-core/src/declaration_engine/declaration_engine.rs index 07ff1e18452..55a08d40833 100644 --- a/sway-core/src/declaration_engine/declaration_engine.rs +++ b/sway-core/src/declaration_engine/declaration_engine.rs @@ -8,12 +8,12 @@ use crate::{concurrent_slab::ConcurrentSlab, language::ty}; use super::{declaration_id::DeclarationId, declaration_wrapper::DeclarationWrapper}; lazy_static! { - static ref DECLARATION_ENGINE: DeclarationEngine = DeclarationEngine::default(); + pub static ref DECLARATION_ENGINE: DeclarationEngine = DeclarationEngine::default(); } /// Used inside of type inference to store declarations. #[derive(Debug, Default)] -pub(crate) struct DeclarationEngine { +pub struct DeclarationEngine { slab: ConcurrentSlab, // *declaration_id -> vec of monomorphized copies // where the declaration_id is the original declaration @@ -249,7 +249,7 @@ pub(crate) fn de_clear() { DECLARATION_ENGINE.clear() } -pub(crate) fn de_look_up_decl_id(index: DeclarationId) -> DeclarationWrapper { +pub fn de_look_up_decl_id(index: DeclarationId) -> DeclarationWrapper { DECLARATION_ENGINE.look_up_decl_id(index) } diff --git a/sway-core/src/declaration_engine/declaration_wrapper.rs b/sway-core/src/declaration_engine/declaration_wrapper.rs index d770c63a485..e0ed238ba0a 100644 --- a/sway-core/src/declaration_engine/declaration_wrapper.rs +++ b/sway-core/src/declaration_engine/declaration_wrapper.rs @@ -12,7 +12,7 @@ use crate::{ /// The [DeclarationWrapper] type is used in the [DeclarationEngine] /// as a means of placing all declaration types into the same type. #[derive(Clone, Debug)] -pub(crate) enum DeclarationWrapper { +pub enum DeclarationWrapper { // no-op variant to fulfill the default trait Unknown, Function(ty::TyFunctionDeclaration), diff --git a/sway-core/src/language/parsed/declaration/trait.rs b/sway-core/src/language/parsed/declaration/trait.rs index 70dd4705372..df44958102a 100644 --- a/sway-core/src/language/parsed/declaration/trait.rs +++ b/sway-core/src/language/parsed/declaration/trait.rs @@ -16,8 +16,8 @@ pub struct TraitDeclaration { } #[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub(crate) struct Supertrait { - pub(crate) name: CallPath, +pub struct Supertrait { + pub name: CallPath, } impl Spanned for Supertrait { diff --git a/sway-core/src/language/ty/declaration/abi.rs b/sway-core/src/language/ty/declaration/abi.rs index 6531382fe0d..823829e9a67 100644 --- a/sway-core/src/language/ty/declaration/abi.rs +++ b/sway-core/src/language/ty/declaration/abi.rs @@ -15,10 +15,10 @@ pub struct TyAbiDeclaration { // NOTE: It may be important in the future to include this component #[derivative(PartialEq = "ignore")] #[derivative(Eq(bound = ""))] - pub(crate) methods: Vec, + pub methods: Vec, #[derivative(PartialEq = "ignore")] #[derivative(Eq(bound = ""))] - pub(crate) span: Span, + pub span: Span, pub attributes: transform::AttributesMap, } diff --git a/sway-core/src/language/ty/declaration/constant.rs b/sway-core/src/language/ty/declaration/constant.rs index e2a51aedbe8..9ba75e79a3e 100644 --- a/sway-core/src/language/ty/declaration/constant.rs +++ b/sway-core/src/language/ty/declaration/constant.rs @@ -9,7 +9,7 @@ use crate::{ pub struct TyConstantDeclaration { pub name: Ident, pub value: TyExpression, - pub(crate) visibility: Visibility, + pub visibility: Visibility, pub attributes: transform::AttributesMap, pub span: Span, } diff --git a/sway-core/src/language/ty/declaration/enum.rs b/sway-core/src/language/ty/declaration/enum.rs index 0e0a3fcbc9b..c4c83f5fca9 100644 --- a/sway-core/src/language/ty/declaration/enum.rs +++ b/sway-core/src/language/ty/declaration/enum.rs @@ -11,7 +11,7 @@ pub struct TyEnumDeclaration { pub type_parameters: Vec, pub attributes: transform::AttributesMap, pub variants: Vec, - pub(crate) span: Span, + pub span: Span, pub visibility: Visibility, } @@ -107,7 +107,7 @@ pub struct TyEnumVariant { pub initial_type_id: TypeId, pub type_span: Span, pub(crate) tag: usize, - pub(crate) span: Span, + pub span: Span, pub attributes: transform::AttributesMap, } diff --git a/sway-core/src/language/ty/declaration/function.rs b/sway-core/src/language/ty/declaration/function.rs index d783a2ead60..2f5f8255d3f 100644 --- a/sway-core/src/language/ty/declaration/function.rs +++ b/sway-core/src/language/ty/declaration/function.rs @@ -22,10 +22,10 @@ pub struct TyFunctionDeclaration { /// Used for error messages -- the span pointing to the return type /// annotation of the function pub return_type_span: Span, - pub(crate) visibility: Visibility, + pub visibility: Visibility, /// whether this function exists in another contract and requires a call to it or not - pub(crate) is_contract_call: bool, - pub(crate) purity: Purity, + pub is_contract_call: bool, + pub purity: Purity, } impl From<&TyFunctionDeclaration> for TyAstNode { diff --git a/sway-core/src/language/ty/declaration/impl_trait.rs b/sway-core/src/language/ty/declaration/impl_trait.rs index 47b45236b3e..8f086e1de2e 100644 --- a/sway-core/src/language/ty/declaration/impl_trait.rs +++ b/sway-core/src/language/ty/declaration/impl_trait.rs @@ -10,7 +10,7 @@ pub struct TyImplTrait { pub methods: Vec, pub implementing_for_type_id: TypeId, pub type_implementing_for_span: Span, - pub(crate) span: Span, + pub span: Span, } impl CopyTypes for TyImplTrait { diff --git a/sway-core/src/language/ty/declaration/struct.rs b/sway-core/src/language/ty/declaration/struct.rs index 6de4b948ce6..b05982a87ef 100644 --- a/sway-core/src/language/ty/declaration/struct.rs +++ b/sway-core/src/language/ty/declaration/struct.rs @@ -11,7 +11,7 @@ pub struct TyStructDeclaration { pub fields: Vec, pub type_parameters: Vec, pub visibility: Visibility, - pub(crate) span: Span, + pub span: Span, pub attributes: transform::AttributesMap, } @@ -107,7 +107,7 @@ pub struct TyStructField { pub name: Ident, pub type_id: TypeId, pub initial_type_id: TypeId, - pub(crate) span: Span, + pub span: Span, pub type_span: Span, pub attributes: transform::AttributesMap, } diff --git a/sway-core/src/language/ty/declaration/trait.rs b/sway-core/src/language/ty/declaration/trait.rs index 1af55404a56..dcf9d94c484 100644 --- a/sway-core/src/language/ty/declaration/trait.rs +++ b/sway-core/src/language/ty/declaration/trait.rs @@ -18,8 +18,8 @@ pub struct TyTraitDeclaration { // work, so I am just going to exclude it #[derivative(PartialEq = "ignore")] #[derivative(Eq(bound = ""))] - pub(crate) methods: Vec, - pub(crate) supertraits: Vec, + pub methods: Vec, + pub supertraits: Vec, pub visibility: Visibility, pub attributes: transform::AttributesMap, pub span: Span, diff --git a/sway-core/src/semantic_analysis.rs b/sway-core/src/semantic_analysis.rs index 7ab52a45be9..65dd9acc926 100644 --- a/sway-core/src/semantic_analysis.rs +++ b/sway-core/src/semantic_analysis.rs @@ -6,7 +6,6 @@ mod node_dependencies; mod program; pub(crate) mod storage_only_types; mod type_check_context; - -pub(crate) use ast_node::*; +pub use ast_node::*; pub use namespace::Namespace; pub(crate) use type_check_context::TypeCheckContext;