Skip to content
FutureBlitz

    Install

    Testing and tools, 3 of 5

    Documenting code

    Write /// and //! doc comments in Markdown, make their examples tests, and export a package's API with fb doc.

    What you’ll build

    A small library, temps, that converts temperatures, documented with doc comments: a summary for the module, one for each declaration, a Panics section, links between declarations, and examples that fb test runs. Then fb doc writes its API, docs and all, as JSON.

    Doc comments

    A /// comment on the lines right before a declaration documents it, and //! lines at the top of a file document the module. Both are Markdown. The first sentence is the summary, which lists and editor tooltips show; parameters go in backticks.

    project.toml
    [project]
    name = "temps"
    version = "0.1.0"
    type = "library"
    convert.fb
    //! Converting temperatures between Celsius and Fahrenheit.
    
    Import std.core
    
    /// A temperature scale.
    Public Enum Scale
        /// Degrees Celsius, where water freezes at 0.
        CELSIUS
        /// Degrees Fahrenheit, where water freezes at 32.
        FAHRENHEIT
    End Enum
    
    /// `degrees` in Celsius, converted to Fahrenheit.
    ///
    /// # Examples
    ///
    /// ```
    /// Expect(ToFahrenheit(100) == 212)
    /// Expect(ToFahrenheit(-40) == -40)
    /// ```
    Public Function ToFahrenheit(degrees: Float): Float => degrees * 9 / 5 + 32
    
    /// `degrees` on `scale`, converted to the other scale. [ToFahrenheit] converts one way only.
    ///
    /// # Panics
    ///
    /// When `degrees` is below absolute zero on its scale.
    ///
    /// # Examples
    ///
    /// ```
    /// # Local boiling = 100.0
    /// Expect(Convert(boiling, Scale.CELSIUS) == 212)
    /// ```
    Public Function Convert(degrees: Float, scale: Scale): Float
        Select scale
            Case Scale.CELSIUS
                Assert(degrees >= -273.15, "below absolute zero")
                Return ToFahrenheit(degrees)
            Default
                Assert(degrees >= -459.67, "below absolute zero")
                Return (degrees - 32) * 5 / 9
        End Select
    End Function
    Output
         Running 2 tests
    test temps.convert.Convert (example) ... ok
    test temps.convert.ToFahrenheit (example) ... ok
    
    test result: ok. 2 passed; 0 failed; 0 skipped; 0 filtered out; finished in 0.02s

    Headings start sections: # Errors for what a function throws, # Panics for how it can be misused, and # Examples. A name in brackets, like [ToFahrenheit], links to that declaration, and can name a member, [Scale.CELSIUS], or another module’s declaration by its full name.

    Examples are tests

    Each code block under # Examples is a test that fb test compiles and runs, named after its declaration. An example uses the package as its readers would, and sees its Package names too, as the package’s own files do, so a Package declaration’s example runs. It has std.core, std.test, and the module imported for it. A line that starts with # is compiled but not shown in the docs, for setup a reader doesn’t need, like boiling above.

    Running fb test in the package’s folder runs both examples, each a test named after its declaration:

    fb test
    ``` An example that would
    wait on a network or a person goes in a fence marked `fb no_run`, which `fb test` only compiles.
    
    ## Links that name nothing
    
    A link to a declaration that doesn't exist, as after a rename, is a warning from `fb doc`, at its
    place in the comment. Change line 23's `[ToFahrenheit]` to `[Kelvin]`, which nothing declares, and
    `fb doc` says:
    
    ```text
    convert.fb:23:57: warning: 'Kelvin' names nothing that code here sees
       |
    23 | /// `degrees` on `scale`, converted to the other scale. [Kelvin] converts one way only.
       |                                                         ^^^^^^^^

    fb doc

    fb doc

    fb doc writes a package’s public API to build/<name>.json: each module, and each Public declaration with its signature, its file and line, and its doc, split into its summary, sections, links, and examples. -o - writes it to stdout instead. It begins:

    {
      "format": 1,
      "package": "temps",
      "version": "0.1.0",
      "modules": [
        {
          "name": "temps.convert",
          "file": "convert.fb",
          "doc": {
            "summary": "Converting temperatures between Celsius and Fahrenheit.",

    The registry builds a package’s documentation pages from this JSON, and this site’s standard library reference comes from fb doc std. In VS Code, hovering a name shows its doc comment.

    What you learned

    • /// documents the declaration after it, and //! the module, both in Markdown.
    • The first sentence is the summary, and headings like # Examples start sections.
    • [Name] links to a declaration, and fb doc warns about one that names nothing.
    • Examples in doc comments are tests that fb test runs.
    • fb doc writes a package’s API and docs as JSON.

    Report an issue with this page or with FutureBlitz on GitHub.