Packages · Lesson 22.9

Documentation

Source
Describe a public API with /// and /** */ comments and their tags, and generate reference pages from them with rux doc.
You'll need: Comment, Visibility, Method

Ordinary comments are for whoever reads the source. Documentation comments are for whoever uses the code: tools collect them and turn them into reference pages, the kind you read for Io or Math. Once your package has pub items that other packages call, describing them is part of the job. This lesson writes documentation comments for a small Rectangle type and generates its pages with rux doc.

Two spellings, one rule

You met documentation comments briefly in Comment. There are two spellings: /// at the start of each line, or one /** … */ block. Both attach to the declaration directly below them:

/// A rectangle measured in whole units.
pub struct Rectangle {
    /// The horizontal side.
    pub width: int;
    /// The vertical side.
    pub height: int;
}

The block form suits a longer description:

/**
    The space the rectangle covers.
    @returns `width` times `height`
*/
pub func Area(self: &Rectangle) -> int {
    return self.width * self.height;
}

A blank line between the comment and the declaration detaches it. The comment then documents nothing, and the declaration is left undocumented.

The summary and the tags

The first sentence is the summary. Indexes show it on its own, so it should make sense without the rest: "The space the rectangle covers." rather than "Returns this.". After the prose come tags, which give the generator structure to work with:

TagDescribes
@param nameone named parameter
@returnsthe result
@seea related item or a web address
@deprecatedthat the item should no longer be used, and what to use instead

Scaled documents its parameter and its result:

/// Makes a copy with both sides multiplied. The original is left as it was.
/// @param factor what each side is multiplied by
/// @returns the scaled copy
pub func Scaled(self: &Rectangle, factor: int) -> Rectangle {

and Size, an older name kept for callers who still use it, says so and points to its replacement:

/// The space the rectangle covers, under its earlier name.
/// @returns the same value as `Area`
/// @deprecated call `Area`, whose name says what it measures
/// @see `Rectangle::Area`
pub func Size(self: &Rectangle) -> int {

Text inside a documentation comment is Markdown, which is why `Area` comes out as code in the generated page.

Generating the pages

rux doc checks the package as rux check does, then writes the pages to Bin/Docs/, starting at index.html; rux doc --open opens them in your browser. Only pub items appear, because the pages describe what other packages can use. Main, which is not pub, is left out.

flowchart LR
    src["/// and /** */ comments<br/>on pub items"] --> lint["rux lint<br/>warns about missing docs<br/>and unknown tags"]
    src --> doc["rux doc"]
    doc --> pages["Bin/Docs/index.html<br/>one entry per pub item"]

rux lint reads the same comments and warns about two kinds of slip: a pub struct or function with no documentation at all, and a tag it does not know. Neither stops the program compiling — which is exactly why a separate check is useful. Tooling puts rux lint and rux doc next to the other commands.

The program

The whole lesson is one package in the Examples repository. Its comments explain every step.

Src/Main.rux
// Ordinary comments, like this one, are for whoever reads the source. Documentation comments are
// for whoever uses the API: tools collect them and turn them into reference pages. There are two
// spellings, `///` on each line or one `/** ... */` block, and both attach to the declaration
// directly below them. A blank line in between detaches the comment.
//
// The first sentence is the summary, shown on its own in indexes, so it should stand alone. Tags
// come after the prose and give the generator structure to work with:
//
//     @param name ...      one named parameter
//     @returns ...         the result
//     @see ...             a related item or a web address
//     @deprecated ...      what to use instead
//
// `rux doc` writes the pages to `Bin/Docs/`, starting at `index.html`; `rux doc --open` opens them.
// Only `pub` items appear, because the pages describe what other packages can use.
//
// Tag names are exact. Write `@return` instead of `@returns`, and the program still compiles, but
// `rux lint` warns "unknown documentation tag '@return'" and `rux doc` refuses to generate the
// page until it is fixed. `rux lint` also warns about any `pub` item left without documentation.
import Io::PrintLine;

/// A rectangle measured in whole units.
pub struct Rectangle {
    /// The horizontal side.
    pub width: int;
    /// The vertical side.
    pub height: int;
}

extend Rectangle {
    /**
        The space the rectangle covers.
        @returns `width` times `height`
    */
    pub func Area(self: &Rectangle) -> int {
        return self.width * self.height;
    }

    /// Makes a copy with both sides multiplied. The original is left as it was.
    /// @param factor what each side is multiplied by
    /// @returns the scaled copy
    pub func Scaled(self: &Rectangle, factor: int) -> Rectangle {
        return Rectangle { width: self.width * factor, height: self.height * factor };
    }

    /// The space the rectangle covers, under its earlier name.
    /// @returns the same value as `Area`
    /// @deprecated call `Area`, whose name says what it measures
    /// @see `Rectangle::Area`
    pub func Size(self: &Rectangle) -> int {
        return self.Area();
    }
}

func Main() -> int {
    let tile = Rectangle { width: 3, height: 2 };
    let floor = tile.Scaled(4);
    PrintLine("tile  {} x {} covers {}", tile.width, tile.height, tile.Area());
    PrintLine("floor {} x {} covers {}", floor.width, floor.height, floor.Area());
    return 0;
}

Run it

cd Examples/Packages/Documentation
rux run
tile  3 x 2 covers 6
floor 12 x 8 covers 96

Then generate the pages, which land in Bin/Docs/index.html:

rux doc --open

Common mistakes

A misspelt tag.
Tag names are exact. Write @return instead of @returns and the program still compiles, but rux lint warns unknown documentation tag '@return', and rux doc refuses with error: invalid documentation: unknown documentation tag '@return' until you fix it.
A blank line under the comment.
Leave an empty line between /// A rectangle measured in whole units. and the struct, and rux lint reports two warnings: documentation comment is not attached to an item, and public struct 'Rectangle' has no documentation comment.
A public function with no documentation.
It compiles, and the pages list it with nothing to say. Add a bare pub func Half(value: int) -> int to this program and rux lint catches it: warning: public function 'Half' has no documentation comment, and its help line suggests a comment above the declaration.
Documenting private items and expecting them in the pages.
rux doc writes pages for pub items only. Its --document-private-items option includes the rest, which can help while you work on the package itself.

Try it yourself

  1. Run rux doc --open and find Size in the pages. How is its @deprecated tag shown?
  2. Add a documented pub method Perimeter with a @returns tag, and regenerate the pages.
  3. Misspell @param as @parameter and run rux lint, then rux doc. What does each say?
  4. Run rux doc --document-private-items. What appears that was missing before?

Learn more