Provide :doc information through a builtin function #999

Open
opened 2025-10-01 15:31:14 +00:00 by minijackson · 6 comments

I'm writing a documentation system for Nix, and I'd like to be able to fetch the documentation of bindings. Currently you can start a REPL and type :doc lib.something, but there's no way to export this value through a Nix expression.

Describe the solution you'd like

Something like this?:

let
  lib = {
    /** My documentation */
    myBinding = 42;
  };
in
  builtins.unsafeGetAttrDoc "myBinding" lib
  # Returns "My documentation", unprocessed, except with indents removed

Describe alternatives you've considered

Nixdoc, or parsing Nix code myself, but these solution seems hacky since the information is known by Nix.

## Is your feature request related to a problem? Please describe. I'm writing a documentation system for Nix, and I'd like to be able to fetch the documentation of bindings. Currently you can start a REPL and type `:doc lib.something`, but there's no way to export this value through a Nix expression. ## Describe the solution you'd like Something like this?: ```nix let lib = { /** My documentation */ myBinding = 42; }; in builtins.unsafeGetAttrDoc "myBinding" lib # Returns "My documentation", unprocessed, except with indents removed ``` ## Describe alternatives you've considered [Nixdoc](https://github.com/nix-community/nixdoc), or parsing Nix code myself, but these solution seems hacky since the information is known by Nix.
Owner

we actually do not have this information. nixdoc is the only reasonable way to do this at the moment, our parser discards this information as soon as it sees it. adding support for this to the parser and exposing it in nixlang would require a bunch of experimental builtins. in the long term this is something we do want to have in some shape. attaching documentation to bindings will cause rather large runtime overhead in the current architecture, so this is going to be a long way out either way.

we actually do not have this information. nixdoc is the only reasonable way to do this at the moment, our parser discards this information as soon as it sees it. adding support for this to the parser and exposing it in nixlang would require a bunch of experimental builtins. in the long term this is something we do want to have in some shape. attaching documentation to bindings will cause rather large runtime overhead in the current architecture, so this is going to be a long way out either way.
Owner

it should be noted that architecturally this is likely fairly possible given that nix-doc (the predecessor of today's :doc) actually supported the exact feature you're talking about, though i wouldn't necessarily say it's as useful as you'd hope.

however, because of various reasons (terminal colour, stability guarantees, rendering format), I'm not sure if reintroducing it would actually do what you want, by itself. this could be hacked in though, it's just the current architecture of things makes it a bit of a hack. i wouldn't necessarily be opposed to it as a feature provided it's clear there are no stability guarantees.

if the desire is building documentation pages, look at nixdoc (without the dash. confusing name collision. my fault).

it should be noted that *architecturally* this is likely fairly possible given that nix-doc (the predecessor of today's :doc) actually supported the exact feature you're talking about, though i wouldn't necessarily say it's as useful as you'd hope. however, because of various reasons (terminal colour, stability guarantees, rendering format), I'm not sure if reintroducing it would actually do what you want, by itself. this could be hacked in though, it's just the current architecture of things makes it a bit of a hack. i wouldn't necessarily be opposed to it as a feature provided it's clear there are no stability guarantees. if the desire is building documentation pages, look at nixdoc (without the dash. confusing name collision. my fault).
Owner

We could easily introduce this however I'm wondering what utility you hope to gain out of it. It introduces API surface that we would theoretically have to support and I'm not that's a good idea here.

We could easily introduce this however I'm wondering what utility you hope to gain out of it. It introduces API surface that we would theoretically have to support and I'm not that's a good idea here.
Author

@lunaphied I think it'd be very useful for API documentation generation. Right now, I've fallen back to using nixdoc, but even something like this currently doesn't work:

{
  subattr = {
    /** Documentation */
    functionToDocument = ...;
  };
}

And it cannot reasonably support evaluating Nix code, so something like this probably won't ever work:

let
  weirdness = _: b: b;
in
weirdness { } {
  /** Documentation */
  functionToDocument = "";
}

It introduces API surface that we would theoretically have to support and I'm not that's a good idea here.

What API surface are you talking about? I understand that the implementation might be difficult in the parser, but from an API standpoint, it should just return the de-indented doc-comment. Am I missing something?

@lunaphied I think it'd be very useful for API documentation generation. Right now, I've fallen back to using `nixdoc`, but even something like this currently doesn't work: ``` nix { subattr = { /** Documentation */ functionToDocument = ...; }; } ``` And it cannot reasonably support evaluating Nix code, so something like this probably won't ever work: ``` nix let weirdness = _: b: b; in weirdness { } { /** Documentation */ functionToDocument = ""; } ``` > It introduces API surface that we would theoretically have to support and I'm not that's a good idea here. What API surface are you talking about? I understand that the implementation might be difficult in the parser, but from an API standpoint, it should just return the de-indented doc-comment. Am I missing something?
Owner

builtins.unsafeGetDocs without compatibility guarantees sounds okay to us. Just… make it something like builtins.unsafeGetDocs { lambda = f; } or something, so we can extend the arguments if need be.

`builtins.unsafeGetDocs` without compatibility guarantees sounds okay to us. Just… make it something like `builtins.unsafeGetDocs { lambda = f; }` or something, so we can extend the arguments if need be.
Owner

we would also need some kind of versioning information on that function or it'll be impossible to detect whether it'll even do what a given expression wants it to do, and if it's impossible to detect it's impossible to change since old code would have to just hope that it won't crash if behavior ever changes. attaching doc comments to lambdas is relatively easy since those have positions we can jump back to, but lambdas have many corner cases: what about multi-argument lambdas, about pattern lambdas, about pattern lambdas with doc comments inside the patterns, what about lambdas that return named lambdas bound elsewhere? even the nixpkgs use of doc comments requires attaching doc comments to bindings, which in turn means that we either need to add runtime fields to attributes to store the reference (which we will not) or that we will have all the nonsensical behavior of unsafeGetAttrPos apply to doc comments as well. even if we decide that the limitations of unsafeGetAttrPos are acceptable we will have to enforce some kind of policy of what is a doc comment, and then some kind of policy to determine when a doc comment actually applies (think lines /** doc comment */, # TODO fix for zero length, substr = ...).

at that point it really feels like the smarter choice would be an unsafeParse that returns a versioned AST (including comments) annotated with position information. that would be useful for other things as well (like pretty-printing of nix expressions in nixos docs), more general introspection of expressions (eg for nixos option type check error reports), or even for ide tools (which would not have to bring their entirely own parser that will almost certainly be non-compliant)

we would also need some kind of versioning information on that function or it'll be impossible to detect whether it'll even do what a given expression wants it to do, and if it's impossible to detect it's impossible to *change* since old code would have to just hope that it won't crash if behavior ever changes. attaching doc comments to *lambdas* is relatively easy since those have positions we can jump back to, but lambdas have many corner cases: what about multi-argument lambdas, about pattern lambdas, about pattern lambdas with doc comments inside the patterns, what about lambdas that return *named* lambdas bound elsewhere? even the nixpkgs use of doc comments requires attaching doc comments to bindings, which in turn means that we either need to add runtime fields to attributes to store the reference (which we will not) or that we will have all the nonsensical behavior of `unsafeGetAttrPos` apply to doc comments as well. even if we decide that the limitations of `unsafeGetAttrPos` are acceptable we will have to enforce some kind of policy of what *is* a doc comment, and then some kind of policy to determine when a doc comment actually applies (think lines `/** doc comment */`, `# TODO fix for zero length`, `substr = ...`). at that point it really feels like the smarter choice would be an `unsafeParse` that returns a versioned AST (including comments) annotated with position information. that would be useful for other things as well (like pretty-printing of nix expressions in nixos docs), more general introspection of expressions (eg for nixos option type check error reports), or even for ide tools (which would not have to bring their entirely own parser that will almost certainly be non-compliant)
Sign in to join this conversation.
No milestone
No project
No assignees
5 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lix-project/lix#999
No description provided.