-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
nix repl: Provide documentation from comment when evaluating to lambda #1652
Conversation
Really nice improvement :) It may be more natural to have this printed only with the |
!!!
…On Wed, Nov 1, 2017 at 09:23 Théophane Hufschmitt ***@***.***> wrote:
Really nice improvement :)
It may be more natural to have this printed only with the :t command (or
maybe a new :doc one) rather than when evaluating, what do you think of
this?
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#1652 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AAErrKM-SIV-_TjoTagh8JTI-8Fawr2zks5syCpkgaJpZM4QNBS2>
.
|
Oops.. i didn't realize the PR was already open and commented on your fork instead.. well you can see it anyway ;) |
src/nix/comment.cc
Outdated
|
||
std::string rawComment = matches[1]; | ||
std::string name = matches[2]; | ||
int timesApplied = countLambdas(matches[3]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think matches could be of length 3 here and you are accessing the 4th element? Since you check for < 3 in line 126..
src/nix/comment.cc
Outdated
} | ||
|
||
// SLOW, probably O(n^2) | ||
std::string stripPrefix(std::string prefix, std::string s) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about something like this:
std::string stripPrefix(std::string prefix, std::string s) {
std::string::size_type index = s.find(prefix);
return (index == 0) ? s.erase(0, prefix.length()) : s;
}
Untested but I guess that should work?! ;-)
src/nix/comment.cc
Outdated
regex_search(sourcePrefix, matches, e); | ||
|
||
std::stringstream buffer; | ||
if (matches.length() < 3) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really don't know what kind of standards the nix code base has in general.. i usually try to avoid "magic numbers" and would introduce some #define
here or so.. same for the indices to access matches below. Just a thought.
@roberth I wonder if this should perhaps restricted to not just any kind of {
/*nixdoc
Proofs that P = NP
*/
proofThingy = p: np: 42;
} Otherwise we might end up with arbitrary things like This would definitely also help for automatically extracting docstrings like these from nixpkgs. In fact - maybe we could go even further and use |
aa530fb
to
d05552b
Compare
@gilligan , thank you for your review. I have implemented all of your suggestions except the I'm also a bit concerned about the ugly comments that might show up. I'm not sure what the special syntax should be. |
@roberth right, having the function name duplicated in the comment might easily go out of sync. Then again: doesn’t that also apply to any kind of comment written for any function anyway? So the function documentation can always be wrong but if we have some tag we could maybe limit the output to the “right” kind of comment blocks? In short: IMHO looking for “/** nixdoc” might be preferable. |
@gilligan I agree that we need some kind of marker to indicate that it's a documentation comment.
We can remove the
What I like about this is that it is very clear that we're using markdown here, it will be shown in the repl without the I'm not saying that we should do Markdown in the future, though. There's no standard for it. At least Restructured Text has a better specification (from what I've heard). Markdown is rather popular though. |
Sounds good to me! Can't wait! ^_^ |
Before the holidays, I put out a twitter poll about the syntax for documentation comments. The results: It got 24 votes:
So, there is no clear winner, but a combination of If anyone is going to decide the color of this bike shed I think it should be @edolstra because I don't want to ruin his awesome creation with ugly comments. |
I'm not in favor of Markdown. |
@edolstra good. What about an extensible syntax marked by a keyword right after |
src/nix/comment.cc
Outdated
|
||
return parseDoc(buffer.str()); | ||
} catch (std::exception e) { | ||
std::cout << "Caught exception: " << e.what() << std::endl; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
=> ignoreException()
.
src/nix/comment.cc
Outdated
i++) { | ||
buffer << line << "\n"; | ||
} | ||
buffer << line.substr(0, pos.column-1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be simplified to something like for (auto & line : tokenizeString(readFile(pos.file), "\n") { ... }
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tokenizeString
didn't work because it doesn't give back the empty lines. I didn't find another suitable function in util either, so I have factored the thing out instead. The other review items are now solved.
src/nix/comment.hh
Outdated
// | ||
// Will return empty values if nothing can be found. | ||
// For its limitations, see the docs of the implementation. | ||
struct Doc lookupDoc(Pos & pos); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be const Pos & pos
.
src/nix/comment.cc
Outdated
#include "comment.hh" | ||
#include "util.hh" | ||
|
||
// This module looks for documentation comments in the source code. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't use //
in the Nix code base except for single-line comments.
d05552b
to
c9f618d
Compare
Before this can be merged we have to make decisions about syntax, because this code will return FIXME comments and such. |
I marked this as stale due to inactivity. → More info |
I'm not really fond of abusing comments for documentation. That's what you do when you don't control the language and don't have a way to add documentation in a more structured way (e.g. C++ and doxygen). But we have the freedom to add documentation in a "proper" way (e.g. as annotations). |
What makes use abuse? Aren't comments equally controllable? By turning comments into annotations, you enable static introspection, so that you don't have to burden the interpreter with it. Evaluation performance is already a problem and Adding documentation at the value level is what you do when you don't control the language tooling and don't have a way to add documentation in a way that works without having to evaluate code. After all, documentation is not a runtime concept but one that relates directly to the source code. |
I'm really in favor of this change (yay for more static and less evaluation, yay for less functors), but I really think we should have a dedicated team of people that discuss the syntax, independently of the implementation. |
Shoving documentation into comments and then trying to correlate the comment with some nearby identifier is the sort of hack you do when you can't add documentation as first-class syntax (e.g. Python annotations). At the least, documentation comments should have some distinguishing syntax (like doxygen comments), since it's not obvious in
that the comment In Nix, documentation can only be done at the value level (as in the NixOS module system) because we don't have a proper module system. So we can't actually statically extract from a source file how the functions in that file are supposed to be called. E.g. we have no way to figure out from an attribute like
that this defines a function that can be called as |
Even if you want to do an implementation that is more value/run time based, there are alternative proposals that would not make it a functor-exclusive feature: #5527 (comment) |
We need this (native doc support). A diversity of solutions isn't adding ecosystem (research) value any more at this point in time. To the contrary. |
We should do this. We were also talking in the documentation team about need for better API docs in the code so they do not get out of sync CC @fricklerhandwerk @infinisil. @edolstra I understand your concerns about hijacking comments being ugly, but I think it is a good place for iteration. Consider these examples:
Basically, I think it will take a few rounds of experimentation to figure out exactly the system we want. The fact that we are so expression-oriented means that we cannot easily steal ideas from other languages with a "static top-level" as-is. If we go for dedicated syntax immediately, we tie our hands with comparability concerns. Conversely, if we "steal" some comment syntax we can be sure that we are backwards compatible. In this manner we can have a low-impact unstable feature will both allow us to start written extremely important API documentation immediately, and also give us plenty of flexibility to experiment with different variations. Once we decide what we want, we can introduce dedicated syntax, and only stabilize that --- not the interim comment syntax. Existing API documentation (which is hopefully much more comprehensive at that point than it is today!) can then be transitioned over to using the new system. This is the lowest risk plan which allows us to accelerate documentation contribution the soonest. Let's do it! |
CC @mightyiam who was looking to work on this very problem! |
Thank you, @Ericson2314 . Tracking issue seems to be #228 (comment). Will provide updates there. If anyone is willing to answer some questions we may have, please subscribe to that issue. |
This pull request has been mentioned on NixOS Discourse. There might be relevant details there: https://discourse.nixos.org/t/how-to-generate-documentation-from-arbitrary-nix-code/22292/9 |
Discussed in the Nix team meeting 2023-01-20:
|
This pull request has been mentioned on NixOS Discourse. There might be relevant details there: https://discourse.nixos.org/t/2023-01-20-nix-team-meeting-minutes-25/25432/1 |
// this is crucial information. | ||
int timesApplied; | ||
|
||
Doc(std::string rawComment, std::string comment, std::string name, int timesApplied) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would like to prefer the C++ way writing ctors
Doc(std::string rawComment, std::string comment, std::string name, int timesApplied) { | |
Doc(std::string rawComment, std::string comment, std::string name, int timesApplied) : rawComment(std::move(rawComment), comment(std::move(comment)), name(std::move(name)), timesApplied(timesApplied) { |
and remove this-> ... = ...
assignments.
We are using copy constructors for std::string
, this is inefficient because C++ string are really "value-like" strings, not references, the buffer will be copied each time if we do
this->name = name
|
||
/* Try to recover a Doc by looking at the text that leads up to a term | ||
definition */ | ||
static struct Doc parseDoc(std::string sourcePrefix) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather like to preserve comment information in our lexer, not using regexes here. That is, we store pointers to comments here, and construct & strip indentation after parsing state.
Lines 305 to 306 in 9428d7d
\#[^\r\n]* /* single-line comments */ | |
\/\*([^*]|\*+[^*/])*\*+\/ /* long comments */ |
This has no overhead but we must carefully deal with the lifetime of these references/pointers.
I think we should do this in the lexer because it will be flexible for further changes & keep consistent with the lexer.
This module does not support tab ('\t') characters. In some places | ||
they are treated as single spaces. They should be avoided. | ||
*/ | ||
namespace nix::Comment { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
namespace nix::Comment { | |
namespace nix::comment { |
nit pick (NFC)
} | ||
|
||
/* See lambdas in parseDoc */ | ||
static int countLambdas(std::string piece) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
static int countLambdas(std::string piece) { | |
static int countLambdas(const std::string &piece) { |
std::ostream & printValue(std::ostream & str, Value & v, unsigned int maxDepth, ValuesSeen & seen); | ||
|
||
// Only prints if a comment is found preceding the position. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Only prints if a comment is found preceding the position. | |
/// Only prints if a comment is found preceding the position. |
|
||
struct Doc { | ||
|
||
// Name that the term is assigned to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Name that the term is assigned to | |
/// Name that the term is assigned to |
|
||
static std::string readFileUpToPos(const Pos & pos) { | ||
|
||
std::ifstream ifs(static_cast<const std::string>(pos.file)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we read from the buffer instead? Because for static analysis tooling, actually there is no such file on the filesystem, this is a problem we deal with #6530
This pull request has been mentioned on NixOS Discourse. There might be relevant details there: https://discourse.nixos.org/t/2024-07-08-nix-team-meeting-minutes/49099/1 |
This provides limited support for python-like docstrings in the
nix repl
. When the user evaluates an expression to a lambda,nix repl
will now print the contents of a documentation comment, as long the comment written right before the attribute, and the attribute value is an actual lambda.Current limitations:
Demo:
Changes:
nix-repl.sh
comment.cc
for the documentation retrieval logicrepl.cc
Todo:
:doc
displays the documentation for the function