nixpkgs: poor mans doc blocks wip / rfc / etc#27394
nixpkgs: poor mans doc blocks wip / rfc / etc#27394grahamc wants to merge 18 commits intoNixOS:masterfrom
Conversation
|
@grahamc, thanks for your PR! By analyzing the history of the files in this pull request, we identified @zimbatm, @edolstra and @Profpatsch to be potential reviewers. |
|
Tough one. It does make the generated documentation nice to read, but less so the source itself. On the other hand, it also makes the doc available in nix-repl, which is nice. I'm leaning towards +1 :-) |
lib/lists.nix
Outdated
There was a problem hiding this comment.
type could be his own attribute, it could be re-used for runtime checks as well
lib/lists.nix
Outdated
There was a problem hiding this comment.
what would you think of using a markdown syntax instead?
|
It would be nice if the function could embed the metadata somehow, that would allow to query any function and not have to worry of it's location or where it came from. But that would require a language change. Even in it's current implementation I think that it's an improvement over writing XML. |
|
Theoretically, nix could be extended to parse documentation comments and expose them via some Syntax within the docs seem a partially independent topic. Maybe it would be simpler to read if we didn't solve syntax at nix layer (i.e. write just string lines) and left parsing to the docs generator, perhaps some standard format (markdown, asciidoc, ...). |
|
I quite like the idea of doing it without language features, so the functionality can evolve on its own. That said, I'm not particularly attached to any specific method. I do actually like #23505 better than this, if we could find a way to make it more performant... and of course, joepie thinks in-code docs are not good at all. |
|
I started thinking about a comment-parsing implementation for the
This indeed looks awesome. Not sure why |
nix doesn't bind the function to the object's context so there wouldn't be a way to parameterize the function. It's more obvious with |
If comments can be introspected they become part of the code and need to be tested like the rest of the code. It's probably better to keep comments free-form and add a notion of introspectable object annotation to the language. That way comments are reserved to the developer to better understand the code and it's possible to extend the language with more information. Nix values are already a big union struct so more data could be bolted-on. |
|
For today, I think that @grahamc's proposal is a good solution for technical code that needs to stay in sync with the code. When/if the language gets extended the documentation can get refactored. |
|
Maybe an example like should be That way we get more flexibility in rendering the examples, and they can be tested automatically. |
|
For the record, I use a similar approach to #23505 in styx to generate library documentation and automatic tests, so far it works pretty well with no visible performance impact, but styx codebase size is nowhere close to nixpkgs. It is still very basic and makes function declarations a lot more verbose, but having generated documentation and tests can really help. Example of generated docs: https://styx-static.github.io/styx-site/documentation/library.html |
|
Bringing this back from the dead, I realize I never commited docs.nix :( I'm rebasing on #27797, as I'm assuming that'll merge. |
|
Ok,You won't like how I did this, but: produced: and if the {
testEx = test:
assert test.test.ensure;
let
posTest = builtins.unsafeGetAttrPos "test" test;
posVerify = builtins.unsafeGetAttrPos "ensure" test.test;
fullContent = lib.strings.splitString "\n" (builtins.readFile posTest.file);
endsAtVerify = lib.lists.take posVerify.line fullContent;
onlyTestLines = lib.lists.drop posTest.line endsAtVerify;
onlyTest = lib.strings.concatStringsSep "\n" onlyTestLines;
asExample = builtins.replaceStrings ["ensure = " " == "] ["" "\n=> "] onlyTest;
removeWhitespace = text:
let
lines = lib.strings.splitString "\n" text;
firstLine = builtins.head lines;
normalized = lib.strings.stringAsChars
(c: if c == " " then " " else ".")
firstLine;
normaSplit = lib.strings.splitString "." normalized;
in
lib.strings.concatMapStrings
(line:
let
p = lib.strings.removePrefix (builtins.head normaSplit) line;
in "${p}\n")
lines;
in ({ title, test }: {
inherit title;
body = removeWhitespace asExample;
}) test;
}I've removed this bit of code from my branch. |
Nice, I like that. I’ve been thinking about how/where we should integrate such tests that they fail as fast as possible. We should chat about this. |
a572e7e to
c5b4789
Compare
abd3653 to
a559812
Compare
|
@bjornfor @zimbatm @edolstra @Profpatsch @vcunat @ericsagnes please review the code, and the output:
Thus far I've received very positive feedback about the results. We can achieve these results in other ways. Is this a change we want to make? Do we want to document Fns like this? |
|
here are some totally bogus benchmarks to see if I slowed things down a lot: master: this branch: |
|
I think that the latest changes go a bit too far in terms of verbosity, the function description now takes a full page in my browser. In most cases it's enough to document the parameter names and types and then expand in the description and examples in case it's not clear what those parameters are doing. How about changing the output to be |
|
I don't know if there is a type notation that could be closer to nix code |
|
This might be a bigger debate, but I think there are two issues:
Frankly this doesn't seem very concerning to me. These function documents should probably be split out in to their own HTML pages anyway so as to not make the Nixpkgs manual massively long. I think a function's documentation shouldn't be held to an arbitrary length requirement.
|
|
Note that the approach I've taken here of making docs in code makes the lib functions much too hard to read, and the output too hard to make right. With this proof of concept, I'm throwing it away and am going to explore just writing docbook. |
|
@zimbatm please feel free to discuss further with me, here :) |
|
hmm. I hope you didn't get discouraged because of my comment. I still think it's a great idea and improves what we currently have. How about skipping the types altogether for now? Most of the time, a description with a few well selected examples are enough for the reader to understand the purpose of a function. |
|
No, I wasn't discouraged by your comment. I think the output I have is good. I don't like how it is done in code. |
|
Well, for exceedingly simple combinators like Speed: if I understand this correctly, Overall, the approach seems OK to me. I didn't delve into any details (yet). I would personally fear moving source of the docs too far from the code, even if |
|
I'd like to add support to this effort by saying the lack of easily discoverable docs for lib functions is a frequent gripe for people who don't know what's in @grahamc how could I help with this effort? |

There has been a lot of chatter around doc blocks in Nix. Since Nix is almost purely expression based, it is likely very challenging to actually make that work. We might look in to how JSDoc handles it, as they have similar challenges I think.
However, that is complicated. I decided to get creative and try something simple out. Maybe you'll like it, maybe you'll hate it ;)
Basically, where we have:
I changed it to:
which get dumped to a docbook file and included in the manual. Currently very WIP, I spent an hour on this, looking for ways to solve this that are easier and more actionable than "we need a better parser."
Example result:

What do y'all think?
cc @joepie91 @domenkozar @edolstra @copumpkin @Profpatsch @zimbatm @vcunat @everyoneelse.