forc-doc 0.66.5

Build the documentation for the local package and all dependencies. The output is placed in `out/doc` in the same format as the project.
# Forc Doc

The Sway language documenter.

---

## Quick Start

### Prerequisites

- Must have [`forc`][forc-reference] installed.
- Must be in a directory, or parent directory containing a [`Forc.toml`][manifest-reference] and some Sway code that successfully compiles
- For documentation to appear you need only add doc attributes to documentable items, like so:
  ```sway
  /// 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
  ```sway
  //! Library containing types used for...
  library;
  ```
  Check out the [doc attribute section][sway-reference-attributes-doc] of the Sway reference for more information on how to document Sway code.

If you've installed a distributed toolchain via [`fuelup`][fuelup-docs], 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`.

```sh
$ 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][forc-doc-manual].

To install `forc doc` for development, see the [Getting Started](#getting-started) section under [Contributing](#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

- [`cargo`][install-cargo]
- [`forc`][forc-reference]
- a default, modern browser (older browsers may cause issues)

> **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 a `fuelup` binary. You can also avoid this by executing the `forc doc` binary via `cargo run`, see [Viewing Changes](#viewing-changes).
>
> ```sh
> $ 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:

```sh
$ git clone https://github.com/FuelLabs/sway.git
```

Then move into the newly created `sway` directory, and install `forc doc`:

```sh
$ 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:

```sh
$ 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`](./src/doc/): The documenting phase. Handles analysis of a compiled typed Sway program and collects useful information into `Documents` 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`](./src/render/): Renders the information collected by the documenting phase into HTML and places them into the `out/doc` directory. This phase is intended to be especially friendly to those familiar with building static HTML webpages. The [`horrorshow` library][horrorshow] uses macros to write HTML that look strikingly similar to writing plain HTML.
- [`licenses`](./src/licenses/): Files that must be present in docs generated by `forc doc` for use of fonts, logos or anything pertaining to the project that requires a license.
- [`static.files`](./src/static.files/): Files that must be present in docs generated by `forc doc` in order for styling to take effect, eg CSS, icons & fonts.
- [`tests/data`](./src/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`](./src/doc/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`](./src/render/item/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:

```rust
// 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`.

```rust
// 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`](./src/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`](./src/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`][docs.rs]:

```html
<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:

```rust
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`:

```sh
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 by `forc doc` which can be found in the `out/doc` directory, and choose the option "open with Live Server". Voila!

[forc-reference]: https://fuellabs.github.io/sway/master/book/forc/index.html "forc reference"
[manifest-reference]: https://fuellabs.github.io/sway/master/book/forc/manifest_reference.html "manifest reference"
[sway-reference-attributes-doc]: https://fuellabs.github.io/sway/master/book/reference/attributes.html#doc "the Sway reference - doc attribute usage"
[fuelup-docs]: https://install.fuel.network/master/ "fuelup docs"
[forc-doc-manual]: https://fuellabs.github.io/sway/master/book/forc/plugins/forc_doc.html "forc-doc manual"
[install-cargo]: https://doc.rust-lang.org/cargo/getting-started/installation.html "install cargo"
[horrorshow]: https://docs.rs/horrorshow/latest/horrorshow/ "horrorshow docs"
[docs.rs]: https://docs.rs/