Doc comments

Document members with doc { } and whole files with rootdoc { }, and shot check makes sure every code name you put in backticks still exists.

A doc sits right above what it documents and shows up wherever that is read: in shot outline, shot api and shot map, and as comments in the compiled Luau. Named sections inside a doc hold a system's rules, how to extend it and examples.

Doc

doc { } documents the thing directly below it. One line or several:

doc { The name shown above the player }
public Name : string

doc {
    Moves the player to a spawn point.
    Yields until the character exists.
}
public RespawnAsync(spawn : SpawnLocation)
    ...
end

It works above:

  • class fields, constructors, methods and static functions,
  • module properties and functions,
  • a public type or internal type in Types.shot.
doc { Why a player's coins changed }
public type Reason = "Pickup" | "Shop"

Rootdoc

rootdoc { } documents the whole file. One per file ("a file can only have one rootdoc"). Put it above the declaration:

types script.Parent.Types

rootdoc {
    Cleanup stores connections and callbacks.
    Cleanup:Run() disconnects them all and runs the callbacks.
}
class Cleanup = {
    ...
}

A doc { } above the declaration counts as the rootdoc too, which is the natural way to document a func (it has no members). A file can't have both.

Sections

A doc or rootdoc can hold named sections after its summary:

rootdoc {
    The coins one player owns. `Bank` keeps one for each player in `Bank.Wallets`.

    section Rules {
        A wallet works like a bank account that only the server can write to. Its whole API is
        `Wallet.new`, `Wallet.Empty`, `Wallet.Coins` and `Wallet.Add`. There is no Spend or Set: a
        purchase is an Add with a negative amount, made by the server after it has checked the price.

        Only the server changes coins. A client asks through `BuyRequest` and the server decides,
        because a change made on the client is never saved and would let a player give themselves coins.

        Every change carries a `Reason`, and a reason that isn't in that union doesn't compile:
        "TypeError: Expected this to be '"Pickup" | "Shop"', but got '"Quest"'; none of the union
        options are compatible". Add the value to `Reason` before writing the code that earns with it.
    }
    section Extend {
        - a new way to earn coins: add its name to `Reason`, then call `Wallet.Add` with it on the server.
    }
}
class Wallet = { ... }
  • The text before the first section is the summary; its first sentence is what shot map and shot outline print.
  • section Name { } starts on its own line; braces inside it pair up like in any doc. Each name once per doc ("a doc can only have one section Rules").
  • Three names mean something to shot; any other name is fine and kept for readers:
section Ruleswhat a reader needs to use the system right without opening its files, in full sentences (see WRITING RULES below). shot outline, shot api and shot overview print it in full.
section Extendhow to add to the system: which union to grow, which catalog to register in, which event to subscribe to.
section Exampleshow to call it, as code. Works on member docs too.

An Examples section on a member's doc:

doc {
    Adds coins to a player's wallet, as a pickup.
    section Examples {
        Bank.Give(player, 25)
    }
}
function Give(player : Player, amount : number)

Give every system's entry file (the folder's same-named file) Rules and Extend sections when it has rules like that, so shot api, shot outline and shot overview print them and a reader doesn't have to find them in the source.

Writing rules

Write the Rules of every shared module (a signal, a cleanup list, a value container, a scheduler) and of every system's entry file for a reader who will not open the file. In this order, in full sentences:

  1. 1. What it works like: something the reader already knows ("works like a bank account").
  2. 2. Its whole API, and what it doesn't have ("There is no Spend or Set"), so nobody looks for it.
  3. 3. Every behaviour a reader would get wrong, one paragraph each: what happens, why, what goes wrong when they don't know it, and the exact error it raises.
  4. 4. Contracts with other systems: what must happen in what order, which side is in charge, Roblox gotchas.

A member's own fact goes in its doc's first sentence: shot outline prints that sentence on the member's line.

Code names in backticks

Write a name from the code in backticks, as the Rules above do: Bank.Give, module.Count, self.Coins, Reason, BuyRequest (an instance file), Market/MarketClient.Open, @shot.Wallet. shot check makes sure each one still exists, so a doc can't keep pointing at something renamed or removed:

Wallet/Wallet.shot:11:67   `Wallet.Addd` in this doc: Wallet has no Addd (renamed or removed?)
Wallet/Wallet.shot:4:30   `Nope.Thing` in this doc: Nope names nothing in this project, Roblox or Luau (renamed or removed?)
  • A name may be a declaration (class, module, func, split module) and its members down the chain (Market.Server.Start follows the split to MarketServer), a type, an instance file, a folder, a local of the file, a parameter or local of the documented member, a member of the documented class or module, or a Roblox or Luau name (Players, RemoteEvent:FireClient, table.insert).
  • module works in a module's docs and self in a class's; a trailing () is fine (Market.Server.Start()).
  • Once a chain reaches a field, property or method, the rest is not checked (self.Changed:Fire).
  • Backticks around anything that isn't a plain name ("Pickup", local x = 1, {string}) are only formatting and are not checked. Event names are strings: write them in quotes, "CoinsGiven".

Writing them

  • doc and rootdoc must be the first word on their line.
  • Braces inside are fine as long as they pair up: doc { Returns {Wallet} sorted by coins } works.
  • Blank lines between a doc and its member are allowed.
  • A doc must come before what it documents. Below the declaration, outside any class or module, it is an error: "doc must sit above a member inside a class or module".
  • An unclosed doc { runs on to the next unmatched }, so the error usually reads "module Open is never closed with }" (or "doc { is never closed with }" when no } is left in the file).

Where they show up

  • shot outline: one line per member, its signature then "-- " and its doc's first sentence (left out when it only repeats the signature); a doc's Rules and Extend in full, other sections folded to one line ("-- section Examples (3 lines, --full)"); --full prints every doc whole, above its member. The rootdoc's first sentence and sections go under the file's header.
  • shot api <folder>: the entry file's Rules and Extend sections under the folder line.
  • shot map: the first line of each file's rootdoc, after the names of its sections ([Rules, Extend]).
  • The compiled Luau: --[[$doc ... ]] and --[[$rootdoc ... ]] comments, and comments on the types in the Types module.