From 9e1c320cead802983266e5b5fd9d55d7e40234a6 Mon Sep 17 00:00:00 2001 From: Sophie Dankel <47993817+sdankel@users.noreply.github.com> Date: Wed, 22 Jan 2025 13:40:21 -0800 Subject: [PATCH] fix: Adds generic trait impls to forc-doc (#6850) ## Description Closes https://github.com/FuelLabs/sway/issues/6848 Also adds the visibility keyword `pub` to public functions ## Checklist - [ ] I have linked to any relevant issues. - [ ] I have commented my code, particularly in hard-to-understand areas. - [ ] I have updated the documentation where relevant (API docs, the reference, and the Sway book). - [ ] If my change requires substantial documentation changes, I have [requested support from the DevRel team](https://github.com/FuelLabs/devrel-requests/issues/new/choose) - [ ] I have added tests that prove my fix is effective or that my feature works. - [ ] I have added (or requested a maintainer to add) the necessary `Breaking*` or `New Feature` labels where relevant. - [ ] 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). - [ ] I have requested a review from the relevant team or maintainers. --- forc-plugins/forc-doc/src/doc/mod.rs | 11 +- .../forc-doc/src/render/item/context.rs | 122 +++++++++++++++--- forc-plugins/forc-doc/src/render/util/mod.rs | 5 + .../forc-doc/src/static.files/ayu.css | 4 - .../Forc.lock | 4 +- .../Forc.toml | 2 +- .../src/bar.sw | 12 +- .../src/foo.sw | 0 .../src/lib.sw | 2 +- .../src/tests/expects/impl_trait/mod.rs | 26 ++-- 10 files changed, 137 insertions(+), 51 deletions(-) rename forc-plugins/forc-doc/src/tests/data/{impl_traits_clone => impl_traits_generic}/Forc.lock (52%) rename forc-plugins/forc-doc/src/tests/data/{impl_traits_clone => impl_traits_generic}/Forc.toml (84%) rename forc-plugins/forc-doc/src/tests/data/{impl_traits_clone => impl_traits_generic}/src/bar.sw (66%) rename forc-plugins/forc-doc/src/tests/data/{impl_traits_clone => impl_traits_generic}/src/foo.sw (100%) rename forc-plugins/forc-doc/src/tests/data/{impl_traits_clone => impl_traits_generic}/src/lib.sw (58%) diff --git a/forc-plugins/forc-doc/src/doc/mod.rs b/forc-plugins/forc-doc/src/doc/mod.rs index 2a3db0311cd..0508258198a 100644 --- a/forc-plugins/forc-doc/src/doc/mod.rs +++ b/forc-plugins/forc-doc/src/doc/mod.rs @@ -4,7 +4,10 @@ use crate::{ render::{ item::{components::*, context::DocImplTrait, documentable_type::DocumentableType}, link::DocLink, - util::format::docstring::{create_preview, DocStrings}, + util::{ + format::docstring::{create_preview, DocStrings}, + strip_generic_suffix, + }, }, }; use anyhow::Result; @@ -96,10 +99,12 @@ impl Documentation { DocumentableType::Declared(TyDecl::StructDecl(_)) | DocumentableType::Declared(TyDecl::EnumDecl(_)) | DocumentableType::Primitive(_) => { - let item_name = doc.item_header.item_name.as_str().to_string(); + let item_name = doc.item_header.item_name.clone(); for (impl_trait, _) in impl_traits.iter_mut() { // Check if this implementation is for this struct/enum. - if item_name.as_str() == impl_trait.implementing_for.span.as_str() { + if item_name.as_str() + == strip_generic_suffix(impl_trait.implementing_for.span.as_str()) + { let module_info_override = if let Some(decl_module_info) = trait_decls.get(&impl_trait.trait_name.suffix) { diff --git a/forc-plugins/forc-doc/src/render/item/context.rs b/forc-plugins/forc-doc/src/render/item/context.rs index 3e4a49cc29f..b907f0ec49c 100644 --- a/forc-plugins/forc-doc/src/render/item/context.rs +++ b/forc-plugins/forc-doc/src/render/item/context.rs @@ -16,7 +16,8 @@ use anyhow::Result; use horrorshow::{box_html, Raw, RenderBox, Template}; use std::{collections::BTreeMap, fmt::Write}; use sway_core::language::ty::{ - TyEnumVariant, TyImplSelfOrTrait, TyStorageField, TyStructField, TyTraitFn, TyTraitItem, + TyConstantDecl, TyEnumVariant, TyFunctionDecl, TyImplSelfOrTrait, TyStorageField, + TyStructField, TyTraitFn, TyTraitItem, TyTraitType, }; /// The actual context of the item displayed by [ItemContext]. @@ -586,16 +587,29 @@ impl Renderable for DocImplTrait { } impl Renderable for TyTraitItem { fn render(self, render_plan: RenderPlan) -> Result> { - let item = match self { - TyTraitItem::Fn(item_fn) => item_fn, - TyTraitItem::Constant(_) => unimplemented!("Constant Trait items not yet implemented"), - TyTraitItem::Type(_) => unimplemented!("Type Trait items not yet implemented"), - }; - let method = render_plan.engines.de().get_function(item.id()); - let attributes = method.attributes.to_html_string(); + match self { + TyTraitItem::Fn(decl_ref) => { + let decl = render_plan.engines.de().get_function(decl_ref.id()); + ::clone(&decl).render(render_plan) + } + TyTraitItem::Constant(ref decl_ref) => { + let decl = render_plan.engines.de().get_constant(decl_ref.id()); + ::clone(&decl).render(render_plan) + } + TyTraitItem::Type(ref decl_ref) => { + let decl = render_plan.engines.de().get_type(decl_ref.id()); + ::clone(&decl).render(render_plan) + } + } + } +} + +impl Renderable for TyFunctionDecl { + fn render(self, _render_plan: RenderPlan) -> Result> { + let attributes = self.attributes.to_html_string(); - let mut fn_sig = format!("fn {}(", method.name.as_str()); - for param in &method.parameters { + let mut fn_sig = format!("fn {}(", self.name.as_str()); + for param in &self.parameters { let mut param_str = String::new(); if param.is_reference { write!(param_str, "ref ")?; @@ -614,22 +628,25 @@ impl Renderable for TyTraitItem { )?; } } - write!(fn_sig, ") -> {}", method.return_type.span.as_str())?; + write!(fn_sig, ") -> {}", self.return_type.span.as_str())?; let multiline = fn_sig.chars().count() >= 60; - let method_id = format!("method.{}", method.name.as_str()); + let method_id = format!("method.{}", self.name.as_str()); let impl_list = box_html! { - div(id=format!("method.{}", item.name().as_str()), class="method trait-impl") { - a(href=format!("{IDENTITY}method.{}", item.name().as_str()), class="anchor"); + div(id=format!("{method_id}"), class="method trait-impl") { + a(href=format!("{IDENTITY}{method_id}"), class="anchor"); h4(class="code-header") { + @ if self.visibility.is_public() { + : "pub "; + } : "fn "; a(class="fnname", href=format!("{IDENTITY}{method_id}")) { - : method.name.as_str(); + : self.name.as_str(); } : "("; @ if multiline { - @ for param in &method.parameters { + @ for param in &self.parameters { br; : " "; @ if param.is_reference { @@ -650,7 +667,7 @@ impl Renderable for TyTraitItem { br; : ")"; } else { - @ for param in &method.parameters { + @ for param in &self.parameters { @ if param.is_reference { : "ref"; } @@ -665,7 +682,7 @@ impl Renderable for TyTraitItem { : param.type_argument.span.as_str(); } @ if param.name.as_str() - != method.parameters.last() + != self.parameters.last() .expect("no last element in trait method parameters list") .name.as_str() { : ", "; @@ -673,13 +690,14 @@ impl Renderable for TyTraitItem { } : ")"; } - @ if method.span.as_str().contains("->") { + @ if self.span.as_str().contains("->") { : " -> "; - : method.return_type.span.as_str(); + : self.return_type.span.as_str(); } } } - }.into_string()?; + } + .into_string()?; Ok(box_html! { @ if !attributes.is_empty() { @@ -698,6 +716,68 @@ impl Renderable for TyTraitItem { } } +impl Renderable for TyTraitType { + fn render(self, _render_plan: RenderPlan) -> Result> { + let attributes = self.attributes.to_html_string(); + let trait_type_id = format!("traittype.{}", self.name.as_str()); + let contents = box_html! { + div(id=format!("{trait_type_id}"), class="type trait-impl") { + a(href=format!("{IDENTITY}{trait_type_id}"), class="anchor"); + h4(class="code-header") { + : self.span.as_str(); + } + } + } + .into_string()?; + + Ok(box_html! { + @ if !attributes.is_empty() { + details(class="swaydoc-toggle method-toggle", open) { + summary { + : Raw(contents); + } + div(class="docblock") { + : Raw(attributes); + } + } + } else { + : Raw(contents); + } + }) + } +} + +impl Renderable for TyConstantDecl { + fn render(self, _render_plan: RenderPlan) -> Result> { + let attributes = self.attributes.to_html_string(); + let const_id = format!("const.{}", self.call_path.suffix.as_str()); + let contents = box_html! { + div(id=format!("{const_id}"), class="const trait-impl") { + a(href=format!("{IDENTITY}{const_id}"), class="anchor"); + h4(class="code-header") { + : self.span.as_str(); + } + } + } + .into_string()?; + + Ok(box_html! { + @ if !attributes.is_empty() { + details(class="swaydoc-toggle method-toggle", open) { + summary { + : Raw(contents); + } + div(class="docblock") { + : Raw(attributes); + } + } + } else { + : Raw(contents); + } + }) + } +} + #[derive(Clone, Debug)] /// Represents the type of [Context] for item declarations that have /// fields, variants or methods, and acts as a wrapper for those values for rendering. diff --git a/forc-plugins/forc-doc/src/render/util/mod.rs b/forc-plugins/forc-doc/src/render/util/mod.rs index 66f0e73acf3..c3d1eacdfe0 100644 --- a/forc-plugins/forc-doc/src/render/util/mod.rs +++ b/forc-plugins/forc-doc/src/render/util/mod.rs @@ -1,2 +1,7 @@ //! Utilities for managing edge cases in rendering types and their corresponding documentation. pub mod format; + +/// Strip the generic suffix from a type name. For example, `Foo` would become `Foo`. +pub fn strip_generic_suffix(input: &str) -> &str { + input.split_once('<').map_or(input, |(head, _)| head) +} diff --git a/forc-plugins/forc-doc/src/static.files/ayu.css b/forc-plugins/forc-doc/src/static.files/ayu.css index 00a5e6ec819..7ef21268bcb 100644 --- a/forc-plugins/forc-doc/src/static.files/ayu.css +++ b/forc-plugins/forc-doc/src/static.files/ayu.css @@ -124,10 +124,6 @@ pre, color: #39afd7; } .content span.type, -.content a.type { - color: #39afd7; -} -.content span.type, .content a.type, .block a.current.type { color: #39afd7; diff --git a/forc-plugins/forc-doc/src/tests/data/impl_traits_clone/Forc.lock b/forc-plugins/forc-doc/src/tests/data/impl_traits_generic/Forc.lock similarity index 52% rename from forc-plugins/forc-doc/src/tests/data/impl_traits_clone/Forc.lock rename to forc-plugins/forc-doc/src/tests/data/impl_traits_generic/Forc.lock index 597334f4dff..df29cb38a52 100644 --- a/forc-plugins/forc-doc/src/tests/data/impl_traits_clone/Forc.lock +++ b/forc-plugins/forc-doc/src/tests/data/impl_traits_generic/Forc.lock @@ -1,8 +1,8 @@ [[package]] name = "core" -source = "path+from-root-09A5B00ACCB1BF9C" +source = "path+from-root-BD391496AC0FBC33" [[package]] -name = "impl_traits_clone" +name = "impl_traits_generic" source = "member" dependencies = ["core"] diff --git a/forc-plugins/forc-doc/src/tests/data/impl_traits_clone/Forc.toml b/forc-plugins/forc-doc/src/tests/data/impl_traits_generic/Forc.toml similarity index 84% rename from forc-plugins/forc-doc/src/tests/data/impl_traits_clone/Forc.toml rename to forc-plugins/forc-doc/src/tests/data/impl_traits_generic/Forc.toml index 99a360f4d83..bac643a5c29 100644 --- a/forc-plugins/forc-doc/src/tests/data/impl_traits_clone/Forc.toml +++ b/forc-plugins/forc-doc/src/tests/data/impl_traits_generic/Forc.toml @@ -2,7 +2,7 @@ authors = ["Fuel Labs "] entry = "lib.sw" license = "Apache-2.0" -name = "impl_traits_clone" +name = "impl_traits_generic" [dependencies] core = { path = "../../../../../../sway-lib-core" } diff --git a/forc-plugins/forc-doc/src/tests/data/impl_traits_clone/src/bar.sw b/forc-plugins/forc-doc/src/tests/data/impl_traits_generic/src/bar.sw similarity index 66% rename from forc-plugins/forc-doc/src/tests/data/impl_traits_clone/src/bar.sw rename to forc-plugins/forc-doc/src/tests/data/impl_traits_generic/src/bar.sw index 7bbedd73a99..494f1860c78 100644 --- a/forc-plugins/forc-doc/src/tests/data/impl_traits_clone/src/bar.sw +++ b/forc-plugins/forc-doc/src/tests/data/impl_traits_generic/src/bar.sw @@ -3,26 +3,26 @@ library; use ::foo::{Foo, Baz}; use core::ops::Add; -pub struct Bar {} +pub struct Bar {} -impl Foo for Bar { +impl Foo for Bar { /// something more about foo(); fn foo() {} } -impl Baz for Bar {} -impl Bar { +impl Baz for Bar {} +impl Bar { fn foo_bar() { Self::foo() } } // test dependency impls -impl Add for Bar { +impl Add for Bar { fn add(self, other: Self) -> Self { Bar {} } } -impl core::ops::Subtract for Bar { +impl core::ops::Subtract for Bar { fn subtract(self, other: Self) -> Self { Bar {} } diff --git a/forc-plugins/forc-doc/src/tests/data/impl_traits_clone/src/foo.sw b/forc-plugins/forc-doc/src/tests/data/impl_traits_generic/src/foo.sw similarity index 100% rename from forc-plugins/forc-doc/src/tests/data/impl_traits_clone/src/foo.sw rename to forc-plugins/forc-doc/src/tests/data/impl_traits_generic/src/foo.sw diff --git a/forc-plugins/forc-doc/src/tests/data/impl_traits_clone/src/lib.sw b/forc-plugins/forc-doc/src/tests/data/impl_traits_generic/src/lib.sw similarity index 58% rename from forc-plugins/forc-doc/src/tests/data/impl_traits_clone/src/lib.sw rename to forc-plugins/forc-doc/src/tests/data/impl_traits_generic/src/lib.sw index dc83faa41ce..d4287bf3783 100644 --- a/forc-plugins/forc-doc/src/tests/data/impl_traits_clone/src/lib.sw +++ b/forc-plugins/forc-doc/src/tests/data/impl_traits_generic/src/lib.sw @@ -1,4 +1,4 @@ library; -mod foo; +pub mod foo; mod bar; \ No newline at end of file diff --git a/forc-plugins/forc-doc/src/tests/expects/impl_trait/mod.rs b/forc-plugins/forc-doc/src/tests/expects/impl_trait/mod.rs index 555954ce244..1c0b96337ca 100644 --- a/forc-plugins/forc-doc/src/tests/expects/impl_trait/mod.rs +++ b/forc-plugins/forc-doc/src/tests/expects/impl_trait/mod.rs @@ -30,8 +30,8 @@ fn test_impl_traits_default() { &doc_path, project_name, &expect![[r##" - Bar in bar - Sway
pub struct Bar {}

Implementations

fn foo_bar()

Trait Implementations

fn abi_encode(self, buffer: Buffer) -> Buffer

fn abi_decode(refmut _buffer: BufferReader) -> Self

fn foo()

something more about foo();

-

fn add(self, other: Self) -> Self

fn subtract(self, other: Self) -> Self

"##]], + Bar in bar - Sway
pub struct Bar {}

Implementations

fn foo_bar()

Trait Implementations

pub fn abi_encode(self, buffer: Buffer) -> Buffer

pub fn abi_decode(refmut _buffer: BufferReader) -> Self

pub fn foo()

something more about foo();

+

pub fn add(self, other: Self) -> Self

pub fn subtract(self, other: Self) -> Self

"##]], ); assert_search_js( &doc_path, @@ -169,7 +169,7 @@ fn test_impl_traits_default() { #[test] fn test_impl_traits_no_deps() { let doc_dir_name: &str = "impl_traits_no_deps"; - let project_name: &str = "impl_traits_clone"; + let project_name: &str = "impl_traits_generic"; let command = Command { path: Some(format!("{}/{}", DATA_DIR, project_name)), doc_path: Some(doc_dir_name.into()), @@ -181,13 +181,13 @@ fn test_impl_traits_no_deps() { &doc_path, project_name, &expect![[r##" - Bar in bar - Sway
pub struct Bar {}

Implementations

fn foo_bar()

Trait Implementations

fn abi_encode(self, buffer: Buffer) -> Buffer

fn abi_decode(refmut _buffer: BufferReader) -> Self

fn foo()

something more about foo();

-

fn add(self, other: Self) -> Self

fn subtract(self, other: Self) -> Self

"##]], + Bar in bar - Sway
pub struct Bar<T> {}

Trait Implementations

pub fn abi_encode(self, buffer: Buffer) -> Buffer

pub fn abi_decode(refmut _buffer: BufferReader) -> Self

pub fn foo()

something more about foo();

+

fn foo_bar()

pub fn add(self, other: Self) -> Self

pub fn subtract(self, other: Self) -> Self

"##]], ); assert_search_js( &doc_path, &expect![[ - r#"var SEARCH_INDEX={"impl_traits_clone":[{"html_filename":"trait.Foo.html","module_info":["impl_traits_clone","foo"],"name":"Foo","preview":"","type_name":"trait"},{"html_filename":"trait.Baz.html","module_info":["impl_traits_clone","foo"],"name":"Baz","preview":"","type_name":"trait"},{"html_filename":"struct.Bar.html","module_info":["impl_traits_clone","bar"],"name":"Bar","preview":"","type_name":"struct"},{"html_filename":"index.html","module_info":["impl_traits_clone","bar"],"name":"bar","preview":"","type_name":"module"},{"html_filename":"index.html","module_info":["impl_traits_clone","foo"],"name":"foo","preview":"","type_name":"module"}]}; + r#"var SEARCH_INDEX={"impl_traits_generic":[{"html_filename":"trait.Foo.html","module_info":["impl_traits_generic","foo"],"name":"Foo","preview":"","type_name":"trait"},{"html_filename":"trait.Baz.html","module_info":["impl_traits_generic","foo"],"name":"Baz","preview":"","type_name":"trait"},{"html_filename":"struct.Bar.html","module_info":["impl_traits_generic","bar"],"name":"Bar","preview":"","type_name":"struct"},{"html_filename":"index.html","module_info":["impl_traits_generic","bar"],"name":"bar","preview":"","type_name":"module"},{"html_filename":"index.html","module_info":["impl_traits_generic","foo"],"name":"foo","preview":"","type_name":"module"}]}; "object"==typeof exports&&"undefined"!=typeof module&&(module.exports=SEARCH_INDEX);"# ]], ); @@ -195,14 +195,14 @@ fn test_impl_traits_no_deps() { doc_dir_name, project_name, vec![ - "impl_traits_clone/index.html", - "impl_traits_clone/all.html", - "impl_traits_clone/foo/trait.Foo.html", - "impl_traits_clone/bar/index.html", - "impl_traits_clone/foo/index.html", - "impl_traits_clone/foo/trait.Baz.html", + "impl_traits_generic/index.html", + "impl_traits_generic/all.html", + "impl_traits_generic/foo/trait.Foo.html", + "impl_traits_generic/bar/index.html", + "impl_traits_generic/foo/index.html", + "impl_traits_generic/foo/trait.Baz.html", "search.js", - "impl_traits_clone/bar/struct.Bar.html", + "impl_traits_generic/bar/struct.Bar.html", ], ); }