75 releases (36 breaking)
new 0.66.4 | Nov 3, 2024 |
---|---|
0.65.2 | Oct 7, 2024 |
0.62.0 | Jul 18, 2024 |
0.52.0 | Mar 27, 2024 |
0.31.3 | Nov 30, 2022 |
#167 in Magic Beans
954 downloads per month
5.5MB
107K
SLoC
Contains (WOFF font, 400KB) NanumBarunGothic.ttf.woff2, (WOFF font, 135KB) src/static.files/FiraSans-Medium.woff2, (WOFF font, 130KB) src/static.files/FiraSans-Regular.woff2, (WOFF font, 82KB) SourceSerif4-Bold.ttf.woff2, (WOFF font, 77KB) SourceSerif4-Regular.ttf.woff2, (WOFF font, 45KB) SourceCodePro-It.ttf.woff2 and 3 more.
Forc Doc
The Sway language documenter.
Quick Start
Prerequisites
- Must have
forc
installed. - Must be in a directory, or parent directory containing a
Forc.toml
and some Sway code that successfully compiles - For documentation to appear you need only add doc attributes to documentable items, like so:
/// Defines my contract ABI... abi MyContractABI {}
- You may also document at the module level with the module level doc attribute syntax:
Note: This will only work at the beginning of Sway files
Check out the doc attribute section of the Sway reference for more information on how to document Sway code.//! Library containing types used for... library;
If you've installed a distributed toolchain via fuelup
, you already have everything you need to run forc doc
. Otherwise, you can install forc
& forc doc
via cargo install
, or from fuelup
directly.
The below commands check you have everything necessary to run forc doc
.
$ cd my_fuel_project
$ ls # check Forc.toml exists
# src Forc.toml
$ forc --version # check forc is installed
$ forc doc --version # check forc doc is installed
$ forc doc --open # open docs in default browser
For usage, see the docs.
To install forc doc
for development, see the Getting Started section under Contributing.
Contributing
Welcome! We're glad you're here to help. Below is an overview of the program's design choices, as well as how to build forc doc
and test your changes locally.
Build Requirements
Tip: If you see no changes take effect, it may be due to multiple
forc doc
binaries. To prevent this, remove any pre-existing versions that take precedence, such as afuelup
binary. You can also avoid this by executing theforc doc
binary viacargo run
, see Viewing Changes.$ which forc-doc # ~/.fuelup/bin/forc-doc $ rm ~/.fuelup/bin/forc-doc $ which forc-doc # if it displays nothing, you're good to go!
Getting Started
Clone the sway
repository into your preferred directory:
$ git clone https://github.com/FuelLabs/sway.git
Then move into the newly created sway
directory, and install forc doc
:
$ cd sway
$ cargo install --path forc-plugins/forc-doc
Great! Let's check everything is working as intended. Try running forc doc
on one of the test directories:
$ forc doc --manifest-path src/tests/data/impl_traits --open
If it succeeded, you should be seeing the test docs in your browser.
Development
New language keyword? Want to add a feature? Updating CSS? forc doc
is setup to make development easy.
Design Overview
Each section of the project is labeled to its corresponding functionality.
doc
: The documenting phase. Handles analysis of a compiled typed Sway program and collects useful information intoDocuments
that can be rendered to HTML. This is where to start if you are trying to implement a new Sway language feature, or make some information about an existing feature available for rendering.render
: Renders the information collected by the documenting phase into HTML and places them into theout/doc
directory. This phase is intended to be especially friendly to those familiar with building static HTML webpages. Thehorrorshow
library uses macros to write HTML that look strikingly similar to writing plain HTML.licenses
: Files that must be present in docs generated byforc doc
for use of fonts, logos or anything pertaining to the project that requires a license.static.files
: Files that must be present in docs generated byforc doc
in order for styling to take effect, eg CSS, icons & fonts.tests/data
: This is where edge case Sway code lives. If an edge case bug arises, write a minimal reproduction and place it here to start.
Try running cargo doc
on the forc-doc
project directory for an in-depth look at what each section is responsible for!
The Documenting Phase
Documentable Items
Adding new documentable items is very straight-forward. Documentable items take only two forms, declarations (TyDecl
s) and context (everything else).
Declarations can be added directly to the description phase of the analysis, found in descriptor.rs
. Just add the new TyDecl
to the match arm of from_typed_decl
and fill in the necessary fields for the resulting Descriptor
wrapped Document
, then return it as Documentable
.
Context items, eg fields on structs, variants of an enum etc, must be added to the ContextType
enum, found in context.rs
and collected at the time of its corresponding TyDecl
's analysis. The ContextType
is wrapped by a Context
struct which is later sorted and rendered to the ItemContext
of a RenderedDocument
.
Example:
Let's say that we want to have a new declaration type called CoolNewDecl
, modeled after the StructDecl
but with some special purpose.
First, we would add the context of the declaration to the ContextType
as a variant:
// in context.rs
pub(crate) enum ContextType {
// Add in the new declaration's context type
CoolNewFields(Vec<TyCoolNewField>),
/* ... */
}
Then, match for the new declaration and return the Document
.
// in descriptor.rs
pub(crate) enum Descriptor {
Documentable(Document),
NonDocumentable,
}
impl Descriptor {
pub(crate) fn from_typed_decl(/* ... */) -> Result<Self> {
match ty_decl {
// Add the new declaration to the match arm
ty::TyDecl::CoolNewDecl(ty::CoolNewDecl { decl_id, .. }) => {
let decl = decl_engine.get_cool_new_decl(decl_id);
if !document_private_items && decl.visibility.is_private() {
Ok(Descriptor::NonDocumentable)
} else {
let item_name = decl.call_path.suffix;
let attrs_opt = (!decl.attributes.is_empty())
.then(|| decl.attributes.to_html_string());
// Fill in the context of the new declaration
let context = (!decl.fields.is_empty()).then_some(Context::new(
module_info.clone(),
ContextType::CoolNewFields(decl.fields),
));
Ok(Descriptor::Documentable(Document {
module_info: module_info.clone(),
item_header: ItemHeader {
module_info: module_info.clone(),
friendly_name: ty_decl.friendly_type_name(),
item_name: item_name.clone(),
},
item_body: ItemBody {
module_info,
ty_decl: ty_decl.clone(),
item_name,
code_str: swayfmt::parse::parse_format::<sway_ast::ItemCoolNew>(
decl.span.as_str(),
)?,
attrs_opt: attrs_opt.clone(),
item_context: ItemContext {
context_opt: context,
impl_traits: None,
},
},
raw_attributes: attrs_opt,
}))
}
}
/* ... */
_ => Ok(Descriptor::NonDocumentable),
}
}
}
Once the declarations are collected into a Document
, the Document
can then be rendered. Refer to the from_raw_docs
method on RenderedDocumentation
found in render/mod.rs
for the beginning of the rendering phase. There you can find plenty of examples on how to render Document
s into RenderedDocument
s if you are adding in a new documentable item.
Index File Generation
Index files, such as the AllDocIndex
, ProjectIndex
and ModuleIndex
s, are rendered using only the information gathered from Sway modules. The process for their rendering can also be found in the RenderedDocumentation::from_raw_docs
method. ModuleInfo
is gathered from at point of generating the Documentation
from a TyProgram
, found in doc/mod.rs
. This is the starting point of the entire analytical process, where a TyProgram
is compiled and passed to Documentation::from_ty_program
.
The Rendering Phase
As stated before, rendering is fairly straight-forward in forc doc
, as the HTML is that of a generic webpage.
Let's try writing a small render-side example together, using the horrorshow
library.
Here is the HTML for the search bar on docs.rs
:
<nav class="sub">
<form class="search-form">
<div class="search-container">
<span></span>
<input
class="search-input"
name="search"
autocomplete="off"
spellcheck="false"
placeholder="Click or press ‘S’ to search, ‘?’ for more options…"
type="search"
/>
<div id="help-button" title="help" tabindex="-1">
<a href="../help.html">?</a>
</div>
<div id="settings-menu" tabindex="-1">
<a href="../settings.html" title="settings">
<img
width="22"
height="22"
alt="change settings"
src="../static.files/wheel-7b819b6101059cd0.svg"
/>
</a>
</div>
</div>
</form>
</nav>
Here is the corresponding horrorshow
code that produces the same HTML:
mod search {
use horrorshow::{box_html, RenderBox};
pub(crate) fn generate_searchbar() -> Box<dyn RenderBox> {
box_html! {
nav(class="sub") {
form(class="search-form") {
div(class="search-container") {
span;
input(
class="search-input",
name="search",
autocomplete="off",
spellcheck="false",
placeholder="Click or press ‘S’ to search, ‘?’ for more options…",
type="search"
);
div(id="help-button", title="help", tabindex="-1") {
a(href="../help.html") { : "?" }
}
div(id="settings-menu", tabindex="-1") {
a(href="../settings.html", title="settings") {
img(
width="22",
height="22",
alt="change settings",
src="../static.files/wheel-7b819b6101059cd0.svg"
)
}
}
}
}
}
}
}
}
Now we can call this function anytime we need to generate a searchbar for our webpage!
Viewing Changes
Once you've made some changes, run the forc doc
binary, passing it a path containing a Forc.toml
:
cargo run -- --manifest-path path/to/manifest --open
Tip: VS Code user? Try the Live Server plugin to make viewing changes even easier. It will reload a webpage on updates, so you only need to rebuild the docs (
cargo run -- --manifest-path path/to/manifest
). Just right click the index file of docs produced byforc doc
which can be found in theout/doc
directory, and choose the option "open with Live Server". Voila!
Dependencies
~55–75MB
~1.5M SLoC