How to generate documentation from arbitrary nix code?

Setting out to think about a tool that generates documentation from nix modules, the first thing that we (@a-kenji, @vimal and I) figured out is that whatever values are provided by nix module is determined by evaluation of it. There are no static items. Everything is an expression.

The only non-hacky way (of obtaining documentation from nix code) I can think of, is by evaluating the code. Otherwise, we are resorting to various “well, if the AST is exactly this or that way” and such. And I don’t find that appealing.

Or, should we have static items in the language to solve this problem?

Help?

6 Likes

Well, what I imagine to be most ergonomic from the contributor’s perspective, and closest to what we have already, is to have documentation naturally in the comments. For example:

# make a qux
{
# a foo
  foo
# some bar
, bar
# a baz to use
, baz ? none
}:
# ...

That may come with types:

# make a qux
{
# list of foobles
  foo # :: [String]
# some bar
, bar # :: Bool
# a baz to use
, baz ? none # :: Derivation
}:
# ...

Here is a sample from current code.

It’s funny to see this thread as I was realizing that in nixpkgs many things are documented in source code via comments, but are often not reported in the manual (cf for instance https://github.com/NixOS/nixpkgs/blob/637d01853e699487643ad96d802deae387000723/pkgs/development/haskell-modules/make-package-set.nix#L196). If someone is thinking about a new way to generate doc from code it would be great to have this covered as well! Maybe using ## instead for comments that must end up in doc could be cool as well.

1 Like

I feel I need to clarify my quesion.

The above is my point. Example:

{
  listify = a: [a];
}

Question: how does the documentation generator figure out that the above module evaluates into an attribute set and not, say, a number?

I’m not an expert but what is wrong with evaluating the module? Thanks to laziness I guess that you can only evaluate the parts of the module that are needed for the documentation, and you certainly don’t need to provide meaningfull arguments since the rest of the module will not be evaluated. And I guess that lib.mkOption provides the necessary interface to recover the type that was provided to lib.mkOption, since you define a new option with something like:

options.enableFoo = lib.mkOption {
      type = lib.types.bool;
      default = false;
    };

styx, among others, has explored that question in the past: GitHub - styx-static/styx: Static site generator in Nix expression language.

1 Like

You’ll probably also want to be aware of this draft PR which adds a mechanism for doing this directly into the language. There is also nix-doc.

4 Likes

Nixpkgs function documentation is rendered using nixdoc. (Which is something else than nix-doc.)

@mightyiam a good first issue would be rendering the first comment from a file at the top of the section. I added it to the documentation team agenda.

We encountered it with @infinisil while hacking on @roberth’s source combinators pull request.

2 Likes

My conclusions after further research:

  • Screw the disagreements about syntax (what the heck ?!?)
  • nix-doc is the way to go, short of 1652, even if nixpkgs has other path dependencies in its history
  • The mob programming momentum unfortunately didn’t seem to materialize

Remaining questions:

  • How to marry docs / examples and testing potentially via nixt
  • Similar to how styx did it

While looking into the theory of this, @fricklerhandwerk helped me reach the understanding that due to the dynamic nature of the language, the return type of a function might not be statically analyzable.

Even an approach of partial evaluation by the interpreter doesn’t solve this problem.

What remains to be seen is how frequently this is the case. Can anyone with exposure to a lot of Nix code shed some light, here, please?

Can anyone from nix-doc share their insights, please?

Pinging @tazjin who wrote nixdoc.