-
Notifications
You must be signed in to change notification settings - Fork 17.9k
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
proposal: x/tools/cmd/godoc: add support for sections #18342
Comments
Should we just split huge packages or have better introduction
in package docs and package level example instead?
|
In retrospect, some of these packages should be split apart and doing so will definitely help the situation. The reality is, many packages are currently bloated and are unlikely to shed API surface anytime soon due to compatibility reasons.
|
+1 for some form of sections in godoc. Splitting large packages can make sense, but it's very difficult to do after the fact and a maze of highly interdependent small packages is not necessarily any clearer than a single large one. Even small packages can benefit from sections, too; for example, database/sql is not particularly large, but grouping the Null* types into a single section might provide a small boost in readability. |
It appears that the Another option might be to expand godoc to accept package comments from multiple source files. The package comment(s) in source files without any other declarations would become the top-level "Overview", and the package comment in each other source file would be the section header for the declarations in that file. Declarations in files without their own package comment would be grouped into the main section with the overview. |
For the example of the (I could envision even-finer-grained splits, too: one for the |
I contend that the most useful feature of sections that I want to see is the ability to group a set of
Absolutely agree the pseudo-internal hooks should have been a different package. I believe the exported stuff to help with custom implementations of Message should also be split out. But those pointer-to-primitive functions are so pervasively used (over 200k usages!) that it's pretty annoying needing to import both a To organize the pointer-to-primitive helpers, you could do something similar to Even then, |
Thinking out loud... What if that grouping was determined, perhaps optionally, by source file? Code is frequently already organized this way, with some exceptions, like autogenerated files. And if feels less fragile than relying on any ordering within a file. Perhaps if you still want section headers and more control, there could be zero or one section header per file, which applied to all code in that file, with identical section headers being coalesced. |
The |
I disagree that lots of tiny packages are benign, even with goimports. The user still needs to remember which package contains which function; easy when you have one or two packages, less so when you have a dozen. Obviously, stuffing unrelated functionality into a single package isn't right either. It's a balance. (For the proto package, I'd say the pointer-to-primitive functions should stay--they're small, cheap, and anyone who uses proto2 will want them--but obviously all the internal hooks should get pulled out. Something to work on once type aliases or the moral equivalent become available.) |
In the example I posted, when I was organizing the That said, if we end up going with grouping by file alone, I'd be fine with that approach. |
As a specific example from the net package, it contains eight error types:
Those are currently spread throughout the package index; collecting them into a single section would improve clarity. These type names could be rewritten to use a common prefix (e.g., ErrParse) so that they sort together, but using less-natural names to work around godoc doesn't seem like a good situation. |
Not to mention that's not the convention. It's ErrFoo (or errFoo) for variables, and FooError (or fooError) for types. |
As another data point. The need for sections is not just for bloated packages, but even small packages can benefit from sections. I mentioned earlier the example of the Here's what the GoDoc would look like with sections: binary doc The sections as seen in godoc also matches exactly how they are laid out in source code. |
I agree that godoc could do better. It would be great if we could do better automatically. It's easy to group errors, for example. Are there automatic groupings that we can explore? Alternately, what do other languages do? One thing I do not want to see is CLs moving code around and breaking 'git blame' just so that things appear in a different order in godoc. |
Grouping functions with similar signatures would help with the Unfortunately, I don't see any obvious heuristics that would help with the |
Python has pydoc, which I do not believe has sections. The Python standard library documentation is a separate set of reStructuredText files with internal sections; it does not use pydoc. I don't believe Javadoc has sections. My experience in reading Javadocs does not lead me to consider it a system in need of emulation. |
Someone spends more time in godoc, someone else might spend more time reading source code. Nonetheless, the better/richer/fancier the godoc output gets in terms of anything based on some magic/metadata/markup/... in comments, the less overaly source code comments will be usable without the godoc "interpreter". It's a zero sum game. |
The Haskell Haddock markup language has heading annotations. Rust uses Markdown with some distinguished sections and some special parsing for examples. I believe that ocamldoc keeps comments and declarations in source-file order, but only allows one source file per package. C and C++ have dedicated third-party websites because their documentation story is so poor. 😦 |
I don't see any form of zero-sum game between godoc and source code comments. The proposed section comments read naturally in either location. |
As a step toward solving the underlying problem, what if the mentions of exported names in the package doc comment automatically turned into links? The net package doc has, for example:
Imagine if instead each of those words - Dial, LookupHost, LookupAddr - were hyperlinked to the real thing. Then the package doc would work better and could contain the sections people are talking about, without any new syntax or non-prose. It seems like maybe that should be the first step at least? It's unclear who should work on this. @neild, @dsnet, @bcmills, any takers? -rsc for @golang/proposal-review |
Not just package doc comments, but all comments. |
Automatic linking will result in some words inadvertently becoming links. I don't know if we care about that. I've certainly wished for internal links on a number of occasions. Adding sections to the existing index seems simpler to me than creating an entire second table of contents in the package doc. |
I think that's an interesting and I would like to see that feature as well. ISTM that what you're describing is a form of "bottom-up" documentation. That is, you look at some specific component and the feature helps you answer the question: "how does this relate to all the other components in this package?" However, sections are designed to be a form of "top-down" documentation. That is, they should help answer the questions: "I'm new to this package, how do I even grasp the high-level organization? what's important?" |
The "top-down" documentation should be given via a good package comment. If we can make the appropriate words in that comment link to the relevant pieces elsewhere we can probably go a long way. We do have all the exported identifiers, so it's a matter of being smart about connecting them with the prose. I think it can be done but it may take some experimentation. |
@griesemer. A good package comment is not sufficient for complex packages. For example, even with a nice package comment, it only marginally helps the proto package to be readable. Furthermore, if you spend time writing a good package comment about what is conceptually a "section", then shouldn't that block of text be co-located next to the related identifiers on godoc? |
As a data point: https://godoc.org/github.com/google/gopacket. There was actually time invested into the package documentation for |
There's an existing table of contents; why not include the overview sections table in there?
|
@neild That was my first thought as well, but this way I only had to render the doc to html once and I didn't have to worry about what happens if someone clicks the link but the overview is collapsed. I don't mind changing it but I figured why go to that trouble for a first iteration. |
Change https://golang.org/cl/72890 mentions this issue: |
CL 72890 is my first cut at implementation hotlinks. It's a vastly cleaned up version of my former CL. The |
Any chance we can get this in for go 1.10? It would be really nice to get this in sooner than later. I reckon that this change can go in, as it doesn't affect running programs, but it makes a useful tool much better. Waiting for go 1.11 means waiting until August/Sept 2018 - a very long time away. |
The 1.10 release is in beta and only bug fixes are going in. Aside from that, the code still needs some polishing and to go through code review, both of which is going to take time and most certainly will not complete before 1.10 release. Secondly, suppose the code for this feature gets submitted not long after 1.10 is release, nothing is stopping you from |
Experience report: We're considering defining some exported types as interfaces rather than as concrete types, entirely because interfaces allow related methods to be grouped together in godoc and concrete types don't. e.g., type ContainerType interface {
// NumCats is the number of cats in the container.
NumCats() int
// Cat returns the i'th cat in the container.
Cat(i int) Cat
// NumPigs is the number of pigs in the container.
NumPigs() int
// Pig returns the i'th pig in the container.
Pig(i int) Pig
} This is a different than the sections proposal (which would allow grouping types, but not methods within a type), but seems related. |
Removed the "accepted" label, because what was accepted was something other than sections (and @griesemer's last comment about sections is that there should be a second review round about sections). |
Per #18342 (comment) and following, I thought the plan was:
How far along are we on that plan? I'm confused about what is being re-suggested in this re-proposal. Also we just approved #25449 which will help with sections in the overview comment, which fits well with (2). I'm still skeptical of per-symbol section annotations. |
It's not far off from integration with x/tools/godoc (but won't make it for the Go1.11 cycle). When I looked at what it would take to integrate with other godoc tools (e.g., godoc.org), I came to the sad realization that every tool does front-end rendering in their own way, which makes a change like this lots of work. Arguably, the place that benefits most is in the I wonder whether we should strive to unify the godoc tools first. I'm not saying they should be the same tool, but they are a lot of opportunities for shared code that would make adding godoc features easier. |
It sounds like the plan is to do both #25449 and #25444. At that point more proposals can be made about what is left. But until then it seems like we should close this proposal as having spawned two concrete next steps. I agree about sharing code between godoc, gddo, etc but that probably doesn't need a proposal (it doesn't affect how programmers write doc comments, for example). |
Coming late to this conversation, but does closing this proposal imply that sections will NOT be done? The spawned proposals are good, but they do not easily satisfy the origial premise of being able to guide readers of documentation through an API by pointing them first to the general purpose or most likely used functions, and then to more specialized fuctions. Yes, one could do that through package comments and hotlinking, but sections are easier to maintain. Another reason for sections is to guide various consumers of an API in a large project. Go does not have protection semantecs like other languages, so large groups have to communicate and use conventions to work well together. It would be nice to reinforce these conventions through the documentation. Also, I am concerned about this comment from the original proposal:
The purpose of sections applies to methods as well. The grouping provided by receivers is for programmatic purposes, not for documentation purposes. The need to group parts of an API for the consumers of a receiver is the same. Currently, in order to do this, one could use a naming convention that adds a prefix to the names of functions and methods in order to group them, but I hate having to change an API for something that could be handled with a simple documentation mechanism. I am not sure if tagging was ever considered as an alternate solution to sections to solve this. It looks like the Deprected: keyword is getting some traction, but really in a limited scope. Being able to put a tag in a comment, like Deprecated: or the BUG(): mechanism, and using those to define sections could alternately be used, and also satisfy the Deprecated: problem (but a sort order would need to be added too.) Just some thoughts. |
@spekary Did you review the above discussion? I believe various possible approaches, as well as their pros and cons, are discussed quite exhaustively, starting already from the Summary section in the initial post. There doesn't seem to be a clear winner, all approaches appear have some cons. On first reading, I'm afraid I can't see what your post adds to the discussion, that was not already discussed. As far as I understand, the current idea is to start by implementing the 2 linked proposals, which have the advantage of also fixing/improving some other issues. Afterwards, with some more experience w.r.t. how they work, the situation could potentially be reevaluated. That said, as far as I understand, those proposals will enable de facto realizing the "Alternative Solution C (Forward References)" approach, by thoughtfully composing the "package level" docs for a package in appropriate Sections (a.k.a. Headings). Personally, I suppose this will then become the advised approach for solving this issue. To be a bit more explicit, "Headings" are already supported in package docs, and #25449 aims to show them in ToC. Per #25444, any function/type/... names used in package docs sections would be automatically hotlinked to their definitions. This sounds like it should enable your stated goal, i.e. for the documentation writer to:
|
I did read it. I saw tagging mentioned but not seriously considered, and I think that should be evaluated. Another goal and benefit of godoc is the minimal amount of effort developers need to put in to get reasonably good documentation. By using a locality approach or tagging approach, the developer need do nothing else but make sure a function or method is defined in the right place in the file or given a correct tag. If he/she gets that wrong, running godoc will make it clear whether the new functions appear in the wrong place. Developers tend to work serially, writing code first with minimal doc, and then adding more extensive doc later. Relying on the two linked proposals means developers must maintain documentation in two places. If a developer in a fit of inspiration writes a bunch of new functions, and then later goes back to try to document them, running godoc will not help identify which functions are missing from the package doc, since they will not appear at all. The developer will have to visually compare the package doc with the generated TOC. It will be easy for a developer to miss some, especially in a large project. As the project grows, the header's description will diverge from what is actually in the TOC. Well, that is just a prediction based on my observed human behavior. As you say, we can see how it goes and revisit later. I am hoping that closing this proposal doesn't end it. |
I re-filed sections as a proposal again (see #44447). I revisited some of my prototype work with hotlinking (#25444) and I'm increasingly convinced that it's troublesome since it can't completely eliminate the presence of false-positives, which is harmful because it actively links users to the wrong information. Furthermore, it goes against the Go philosophy that "clear is better than clever". See the end of the proposal in #44447 for detailed arguments against hotlinking. |
Problem Statement
As packages grow organically over time, their API surface often expands to the point where it is hard to understand how to use the package when all functions and types are shown as a vast alphabetically ordered list. Aside from reading examples (and other 3rd party tutorials), it becomes nearly impossible figuring out how to use a package by the
godoc
alone.For example, it is not clear from the
godoc
of theproto
package that the core set of functions areMarshal
,Unmarshal
,Equal
,Clone
, andMerge
apart from reading the example. The vast majority of other functions are for more advanced (and legacy) functionality that should be categorized separately from the core functionality.I propose that
godoc
provides support for user-defined sections.Proposed Solution
(Approach presented here is Solution A)
It is observed that Go code is often written in such a way that declarations that would be grouped together under a section are already located in proximity of each other within the source code. Assuming that most sections follow this pattern, I propose that a special
Section:
marker in a top-level comment be used to signify the start of a section. Allconst
,var
,func
, andtype
declarations below that marker will be considered part of that section. A section will not cover methods since those are already implicitly grouped under the receiver type.The coverage of the section group extends either until:
End section.
marker, which must be a top-level comment with a blank line above and below it. As a matter of style, it will be discouraged practice to use the end marker unless necessary.The section marker syntax allows for a required title and an optional description:
The title is a string that follows the
Section:
magic string and must be one line. The title may be any arbitrary string, but white space will be collapsed together. The description is optional and comprises of zero or more paragraphs (similar to package documentation) and must be preceded by a blank line.For example, the
proto
package contains a set of helper functions that are only used when dealing with proto2 messages. In the source code, they are already co-located near each other with a comment that looks very similar to a section header. This portion could be modified to be more presentable in the public documentation:All of the helper functions (
Float32
,Float64
,Int
,Int32
,Int64
,String
,Uint32
,Uint64
) will be grouped together under this section until the next top-level end marker:// End section.
Sections without source locality
The observation that related declarations are often close together in the source does not always hold true. There are certain rare use cases of sections where the related declarations are not necessarily close by (or even in the same source file).
To solve this use case, we permit multiple section headers that have the same title. All declarations under sections with the same title will be considered to be under the same section. Only one of those section headers may have a description. This feature, combined with the end marker, can be used to indicate which declarations in separate files belong in the same section.
For example, the
proto
package has several types and functions that are only used by generated code and should never be called by user code. These types and functions live in different files. A section can be used to clearly identify these types and functions that are of no interest to the user by add a section inlib.go
:Elsewhere in
message_set.go
:Elsewhere in
properties.go
:How is
godoc
rendered?All
const
,var
,func
, andtype
declarations that do not have a section will be shown at the top of the index as they are currently shown. Methods will always be grouped under their receiver type. The "core functionality" of a package should be a small set of declarations that do not have a parent section so that they appear first in the index.For each section, the title will be printed and then the section's declarations will be shown. The list of sections are ordered alphabetically and the list of declarations are also listed alphabetically in the same way that they are shown today.
See the attached images for an example of what the
proto
package could look like.Questions:
Q: Will there be support for arbitrary ordering of sections?
No. Since sections are ordered alphabetically, it is trivial for the ordering to be defined by prepending the section title with some prefix (e.g., the section number). It is unlikely that the number of sections will exceed 10, but if it does, a leading 0 can be prepended to the prior 9 sections. People are welcome to design their own ordering system (or carefully choose titles such that they appear in the desired order).
Q: Will there be support for sub-sections?
No. The number of indentations needed to visually show nested sections would make
godoc
too unreadable. Again, since the sections are ordered alphabetically, a prefix (e.g.,3.1.
) can be used to create the concept of a "sub-section".Q: What about other approaches to describe a section?
I looked other approaches of describing what was in a section and evaluated possible designs. They are described below.
Alternative Solution B (File-based locality)
(Idea by @josharian)
A variation on the source locality idea is to restrict it such that the section header can only be applied at most once at the top of the source file. The same section title may be present on multiple files. This approach avoids the need for a
End section.
marker since sections effectively span the entire file.Alternative Solution C (Forward References)
An alternative to defining sections by source locality is to use forward references. In this approach, each section header contains a list of all the
consts
,vars
,funcs
, andtypes
that belong in that section.Alternative Solution D (Reverse References)
An alternative to defining sections by source locality is to use reverse references. In this approach, the comment for each
const
,var
,func
, andtype
declaration contains a reference to the parent section it belongs in.Summary
A summary of the possible solutions:
End section.
marker(A, B) In looking at examples of what could use sections, it was common to find a comment block that was very similar to a section header. Unfortunately, the beautiful prose written is not visible on the godoc.
(D) The
Section: Foo title
string will be visible for doc-like tools with no understanding of sections.(B) Since sections are defined on a per-file basis, it may be necessary to move code around, which muddle the code history.
(A, B) In the common case, there is only one section title and renaming is trivial. In rarer cases the same title may be used in multiple places and will need to be updated together.
(C) It is easy for the forward references to get out of sync when declarations are renamed or deleted. This is trivially checked by the
lint
tool.(C) Any approach using references needs to provide that reference information somewhere and specifying these references can be cumbersome.
For forward references, in the event that two section headers references the same declaration, which section should that declaration be in? Also, if there are many identifiers (e.g., lots of constants), do all identifiers have to be listed in the section header? The problem gets worse when different section headers refer to different identifiers within the same declaration block. Should the block be split apart to be in different sections?
For backward references, a reference is still needed, but it avoids the problem with block declarations since the parent section is applied on the entire block.
(A) The end section marker is not always needed since the end of a file or start of a section ends the current section's scope. Forgetting the end section marker can accidentally add more declarations to a section than intended. Getting this wrong is obvious when viewing the godoc.
I propose that
godoc
add support for user-defined sections by source locality (Solution A) as opposed to other approaches (using forward references or reverse references). I believe that Go documentation is often clean and palatable when the package is small, but quickly becomes overwhelming as the number of exported declarations in the package grows. User defined documentation sections solves this problem and encourages clean documentations at scale.Related proposals:
It may be the case that when a package accrues many types over time that they should to be broken off into their own seperate package. However, support for movement of types does not change the fact that these types still exist in the original package for compatibility reasons. If support for #18130 happens, that is more reason that we should support sections so that documentation can clearly identify that a group of types have moved and provide details about the move.
As a possible alternative to #17056, a section could be used to contain all deprecated items under it. However, sections would not be able to group deprecated methods or fields. The existence of both features in
godoc
do not hamper the other, but could be used powerfully augment each other. You can imagine allowinggodoc
to hide an entire section with theDeprecated:
magic string in the section description.The text was updated successfully, but these errors were encountered: