odd #
Keep an opam switch's HTML documentation continuously up to date.
odd wires opam's post-install-commands, pre-remove-commands,
post-remove-commands and post-session-commands hooks so that after every
successful opam install / opam upgrade / opam remove, the docs for
exactly the packages that changed are regenerated, in dependency order, by
odd-driver. The per-package hooks are plain coreutils (a touch to mark a
package stale, find -delete/rm -rf to drop a removed one's docs), so they
keep working even when odd itself isn't installed in the switch; only the
once-per-session worker needs the binary.
Documentation artifacts are installed under $OPAM_SWITCH_PREFIX/lib/: a
module's .odoc/.odocl files sit next to the .cmt/.cmti they were
compiled from, in the library's own directory, and a package's documentation
pages live under lib/<package>/, mirroring their ids — for most packages,
the same directory. HTML output and indexes are written to
$OPAM_SWITCH_PREFIX/odoc/<package>/, with a landing page at
$OPAM_SWITCH_PREFIX/odoc/index.html. (This is odd's own tree, kept separate
from opam's $OPAM_SWITCH_PREFIX/doc, where packages install their own
documentation.)
See doc/DESIGN.md for the full design, and
doc/DESIGN_DOC.md / doc/DESIGN_COMPLETE.md
for the doc and complete commands.
Setup #
$ odd setup # show the opam configuration to be added
$ odd setup --apply # add it to ~/.opam/config via `opam option`
$ odd rebuild --all # Initial (slow!) build of all the docs
The odoc driver (odd-driver) is bundled with odd (vendored under
driver/, built and installed alongside the odd binary), so there's nothing
else to install. It drives the odoc, odoc-md and sherlodoc binaries,
which must be present in the switch.
Commands #
odd sync— session worker (the post-session hook); rebuilds every package marked stale, in dependency order, and regenerates the landing page. Always exits 0; details go to$OPAM_SWITCH_PREFIX/var/cache/odd/log.odd rebuild [--all | PKG...]— mark packages stale and sync. Exits non-zero on failure (user-facing, unlike the hook commands).odd order [PKG...]— print the dependency order that sync would use (debugging aid).odd search [--package PKG]... [--match=name] [-n N] QUERY— search the generated documentation. Queries every package'ssherlodoc_db.marshaltogether (a single query covers the whole switch), printing each match with its owning library and package. The query is space-separated words, optionally followed by a: typeannotation (map : list -> _); words match as literal substrings of an entry's name or its doc text — no regexes. Operators are indexed by their bare symbol (odd search '$'findsCmdliner.Term.($)).--match=namekeeps only entries whose name contains every word;--packagerestricts to named packages; at most-nresults are printed (default 25).odd doc REFERENCE— print an item's documentation as Markdown, under a header naming the item and the library/package it comes from, resolving an odoc reference against the whole switch.Stdlibis open, so bare names work (List,print_endline); references may also be qualified (Astring.String) or path-qualified — module units by library (/brr/Brr,/terminal.ansi/Terminal_ansifor a sub-library), pages by package (/brr/web_page_howto). A reference that fails to resolve is retried against the switch's package/library names (brr.Brr→/brr/Brr,/terminal/ansi/…→/terminal.ansi/…) with a note on stderr; a miss naming an installed package whose docs aren't built points atodd rebuild. A module's rendering ends with its submodule references, since their bodies appear only assig ... endin the parent page, and every rendering ends with the installed source paths (.mli/.ml, a page's.mld) when present. When a reference is ambiguous (e.g. a bareindex, a page in every package), it lists the package-qualified alternatives instead of picking one; when a bare name resolves through theStdlibopen (List→Stdlib.List), a note on stderr names the winner.odd complete PARTIAL— list the referencesPARTIALcould be completed to, one per line: members of what it names so far (List.m→List.map, …; kind tags filter, e.g.module-List.type-→module-List.type-t), package/library names for a leading/(/o→/odoc,/odoc.model, …), or the entries under a/root/path. A root that is both a package and a library (brr) lists both namespaces: module units (Brr) next to pages and sub-library directories (brr.poke); sub-library modules complete under the dotted root (/terminal.ansi/→Terminal_ansi). The engine for shell completion.
Shell completion (zsh, bash) #
Completions are shipped for zsh (completions/zsh/_odd) and bash
(completions/bash/odd). Both complete the subcommands and, for doc and
complete, the reference argument — by calling odd complete, so completion
always matches the command's own resolver (Stdlib open, package-qualified
/pkg/... paths, kind tags like type-, section labels, …). Type . or /
and press TAB again to drill in.
opam install puts both under the package's share dir. Neither directory is
consulted by the shell by default, so point at it yourself.
zsh — add to ~/.zshrc, before compinit:
fpath=("$(opam var odd:share)/zsh" $fpath)
autoload -U compinit && compinit
bash — add to ~/.bashrc:
source "$(opam var odd:share)/bash/odd"
(Or use completions/zsh and completions/bash/odd from a source checkout. The
bash file is named after the command, so dropping it in
~/.local/share/bash-completion/completions/odd works too, and loads lazily.)
Development #
$ opam install . --deps-only --with-test
$ dune build
$ dune runtest
- OCaml 97%
- Raku 3%