Skip to content
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

cmd/doc: add example support #26715

Open
DeedleFake opened this issue Jul 31, 2018 · 22 comments
Open

cmd/doc: add example support #26715

DeedleFake opened this issue Jul 31, 2018 · 22 comments
Labels
FeatureRequest Issues asking for a new feature that does not need a proposal. NeedsFix The path to resolution is known, but the work has not been done. Proposal-Accepted
Milestone

Comments

@DeedleFake
Copy link

To go along with #25443, #25595, and #18807, it would be nice to get support for godoc's -ex flag in go doc.

@ALTree ALTree changed the title cmd/doc: Add example support cmd/doc: add example support Jul 31, 2018
@ianlancetaylor ianlancetaylor added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Aug 3, 2018
@ianlancetaylor ianlancetaylor added this to the Unplanned milestone Aug 3, 2018
@ianlancetaylor
Copy link
Member

CC @robpike

@robpike robpike self-assigned this Aug 3, 2018
@robpike robpike modified the milestones: Unplanned, Go1.12 Aug 3, 2018
@robpike robpike removed the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Nov 8, 2018
@ianlancetaylor ianlancetaylor added NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. FeatureRequest Issues asking for a new feature that does not need a proposal. labels Dec 7, 2018
@ianlancetaylor ianlancetaylor modified the milestones: Go1.12, Unplanned, Go1.13 Dec 7, 2018
@ianlancetaylor ianlancetaylor added NeedsFix The path to resolution is known, but the work has not been done. and removed NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. labels Dec 7, 2018
@ianlancetaylor
Copy link
Member

Punting to 1.13.

@robpike robpike modified the milestones: Go1.13, Go1.14 Jun 16, 2019
@rsc rsc modified the milestones: Go1.14, Backlog Oct 9, 2019
@rsc rsc unassigned robpike Jun 23, 2022
@nickjwhite
Copy link

I've recently started working on implementing this.

I'm wonder whether gating the example output behind the -ex argument is best, or whether go doc should show examples by default. I'd prefer the latter, personally, as if I'm looking up a function or whatever, chances are that if there's an example it's useful and relevant to my understanding how to use the code.

On the other hand, examples can be quite long, particularly whole file examples demonstrating a package (e.g. see the examples of sort: https://pkg.go.dev/sort).

One option could be to always show examples for specific symbols which have been requested, but not for the whole package unless -ex is passed. However that would probably be surprising behavior to most people.

Anyone have any opinions as to what would be best. If not I'll probably proceed with examples only shown when -ex is passed, as it's reasonable, but as I say I personally would prefer always printing them.

@gopherbot
Copy link
Contributor

Change https://go.dev/cl/445116 mentions this issue: cmd/doc: Add -ex flag to show examples

@DeedleFake
Copy link
Author

@nickjwhite

I think that examples should not be shown without the -ex flag, as usually when I'm looking something up it's a quick reference to the documentation or the ordering of the arguments and/or returns or something. I'm not usually looking for a full explanation every time, and I think it makes more sense to not show the examples by default than to require a flag to suppress them instead if they'd be annoying. That being said, I think that it should say if there are examples available, and maybe how many, if the flag isn't passed both for an entire package and when looking up a specific.

@nickjwhite
Copy link

Thanks for your thoughts @DeedleFake, very helpful to hear. I implemented a modified version of your suggestion in the latest change I submitted for review, based on feedback from Rob Pike. Take a look there if you're interested.

@robpike robpike changed the title cmd/doc: add example support proposal: cmd/doc: add example support Nov 22, 2022
@rsc rsc added this to Proposals Nov 22, 2022
@rsc rsc moved this to Incoming in Proposals Nov 22, 2022
@robpike
Copy link
Contributor

robpike commented Nov 22, 2022

Turned into a proposal to sort out the UI, which has not been settled satisfactorily through the CL.

@robpike
Copy link
Contributor

robpike commented Nov 22, 2022

There has been significant discussion in the CL about how examples would appear in go doc output, with no clear resolution.

With no -ex flag present, should the command say when examples exist?

You could put a line of commentary after the type/function/method saying there are examples, but that will get repetitive and unhelpful. The original draft was to suggest the command to get them, but that is also repetitive, repeating most of the information every time, and possibly many times in a given invocation.

You could also annotate each type with the functions that exist. That has the advantage of similarity with the rest of the output, but it too has issues. Consider bytes.Buffer:

$ go doc bytes.Buffer
package bytes // import "bytes"

type Buffer struct {
	// Has unexported fields.
}
    A Buffer is a variable-sized buffer of bytes with Read and Write methods.
    The zero value for Buffer is an empty buffer ready to use.

func NewBuffer(buf []byte) *Buffer
func NewBufferString(s string) *Buffer
func (b *Buffer) Bytes() []byte
func (b *Buffer) Cap() int
func (b *Buffer) Grow(n int)
func (b *Buffer) Len() int
func (b *Buffer) Next(n int) []byte
func (b *Buffer) Read(p []byte) (n int, err error)
func (b *Buffer) ReadByte() (byte, error)
func (b *Buffer) ReadBytes(delim byte) (line []byte, err error)
func (b *Buffer) ReadFrom(r io.Reader) (n int64, err error)
func (b *Buffer) ReadRune() (r rune, size int, err error)
func (b *Buffer) ReadString(delim byte) (line string, err error)
func (b *Buffer) Reset()
func (b *Buffer) String() string
func (b *Buffer) Truncate(n int)
func (b *Buffer) UnreadByte() error
func (b *Buffer) UnreadRune() error
func (b *Buffer) Write(p []byte) (n int, err error)
func (b *Buffer) WriteByte(c byte) error
func (b *Buffer) WriteRune(r rune) (n int, err error)
func (b *Buffer) WriteString(s string) (n int, err error)
func (b *Buffer) WriteTo(w io.Writer) (n int64, err error)
$

There are many examples for this type:

example_test.go:17: func ExampleBuffer() {
example_test.go:25: func ExampleBuffer_reader() {
example_test.go:33: func ExampleBuffer_Bytes() {
example_test.go:40: func ExampleBuffer_Cap() {
example_test.go:50: func ExampleBuffer_Grow() {
example_test.go:59: func ExampleBuffer_Len() {
example_test.go:67: func ExampleBuffer_Next() {
example_test.go:80: func ExampleBuffer_Read() {
example_test.go:98: func ExampleBuffer_ReadByte() {

The first two are bound to the type, so you might get

func NewBuffer(buf []byte) *Buffer
    func ExampleBuffer()
    func ExampleBuffer_reader()

(I indented them for clarity, and maybe that's what should happen.) But what about all the others? They really gum up the output, with every method getting an example,

func (b *Buffer) Bytes() []byte
   func ExampleBuffer_Bytes()
<continuing like this for every method>

And in fairness, the boilerplate isn't adding much. Maybe it would be better to just annotate the default listing, like this:

func NewBuffer(buf []byte) *Buffer     <X>
func NewBufferString(s string) *Buffer    <X>
func (b *Buffer) Bytes() []byte    <X>

with some suitable symbol for <X>.

All this is just one line of inquiry. There is another option, which is to say nothing about examples by default. (I probably prefer that but am far from certain.) Having an -ex flag to trigger them is fine, but consider this plan:

The 'go doc' command excludes the tests, always. What if it sometimes didn't? Then you could ask,

$ go doc -src ExampleBytes

for instance. Or even,

$ go doc -src TestBufferGrowth

and similarly for benchmarks. That possibility should be factored into the way examples (and maybe tests and benchmarks) should be displayed.

There is too much undesigned here to jump to a CL.

@rsc rsc moved this from Incoming to Active in Proposals Nov 30, 2022
@rsc
Copy link
Contributor

rsc commented Nov 30, 2022

This proposal has been added to the active column of the proposals project
and will now be reviewed at the weekly proposal review meetings.
— rsc for the proposal review group

@hherman1
Copy link

hherman1 commented Dec 1, 2022

What if we show examples when you are looking for docs on a specific function, and otherwise never show them? Usually the go doc for a single function is pretty short so there’s room to fill and since it’s more targeted the examples are much more likely to be helpful

@nickjwhite
Copy link

What if we show examples when you are looking for docs on a specific function, and otherwise never show them? Usually the go doc for a single function is pretty short so there’s room to fill and since it’s more targeted the examples are much more likely to be helpful

This doesn't take into account package level examples, which do exist.

Also, just to point out that while it is often true that function level documentation is short, that's not always the case - take time.Parse for example, which prints 48 lines in total, and the associated example for it is 51 lines (including output). This is unusual, but is not unprecedented. That said, I do personally agree with you that always printing examples when doing go doc pkg func would be my preference. When I'm looking up specific documentation, an example is a useful component of that.

@nickjwhite
Copy link

With no -ex flag present, should the command say when examples exist?
...
There is another option, which is to say nothing about examples by default. (I probably prefer that but am far from certain.)

I definitely think that some pointer to there being executable examples available is important. I don't want to run go doc bufio ScanBytes, and think "I could do with an example, I'll try -ex," then do that and be met with no output. That's not a good user experience.

I've been using the version I wrote for the CL day-to-day over the past week, where the text "There are executable examples for " is added to the end of the documentation, which I've found to be perfectly good, and not too annoyingly repetitive.

You could also annotate each type with the functions that exist. That has the advantage of similarity with the rest of the output, but it too has issues. Consider bytes.Buffer:

I don't mind there not being hints that particular functions or types have examples until the precise one is looked up. I think there's no neat way to do this; every option "gums up" things too much, even just annotating the listing like you gave examples for.

The 'go doc' command excludes the tests, always. What if it sometimes didn't? Then you could ask,

$ go doc -src ExampleBytes

Interesting idea, but I think it's a bit less clear & discoverable than showing the example(s) associated with a regular identifier, particularly given that there can be several examples for an identifier. Consider go doc -ex bufio Scanner versus go doc bufio ExampleScanner_emptyFinalToken.

Overall I prefer something close to what's currently in the CL. But I am a little biased (as well as inexperienced), as it's my first contribution to Go, so take what I suggest with an appropriate grain of salt. That said, I chose this issue to start on as I always hated that there was no way to get examples from the command line, and I use go doc all the time, so I am motivated to get a good solution!

@rsc
Copy link
Contributor

rsc commented Jan 4, 2023

It's unclear how to annotate the lines in an unobtrusive manner. The best we came up with in a discussion is:

type Buffer struct {  // example available
	// Has unexported fields.
}
    A Buffer is a variable-sized buffer of bytes with Read and Write methods.
    The zero value for Buffer is an empty buffer ready to use.

func NewBuffer(buf []byte) *Buffer
func NewBufferString(s string) *Buffer
func (b *Buffer) Bytes() []byte  // example available
func (b *Buffer) Cap() int  // example available
func (b *Buffer) Grow(n int)  // example available
func (b *Buffer) Len() int  // example available
func (b *Buffer) Next(n int) []byte  // example available
func (b *Buffer) Read(p []byte) (n int, err error)  // example available
func (b *Buffer) ReadByte() (byte, error)  // example available
func (b *Buffer) ReadBytes(delim byte) (line []byte, err error)
func (b *Buffer) ReadFrom(r io.Reader) (n int64, err error)
func (b *Buffer) ReadRune() (r rune, size int, err error)
func (b *Buffer) ReadString(delim byte) (line string, err error)
...

but that's still pretty obtrusive. If instead we require -ex to start telling about examples, then it would make sense for go doc -ex bytes.Buffer to say:

func NewBuffer(buf []byte) *Buffer
    func ExampleBuffer()
    func ExampleBuffer_reader()
func (b *Buffer) Bytes() []byte
   func ExampleBuffer_Bytes()
...

and then go doc -src ExampleBuffer could print that example, as go doc -src bytes.NewBuffer prints the code for that function today.

The downside is you'd need to know about and use -ex. We thought about things like // -ex but that's pretty cryptic.

@rsc
Copy link
Contributor

rsc commented Jan 11, 2023

How about this for the way to add example support?

(1) The new flag -ex adds examples to the output, with examples attached to the API in the type, like go doc -ex bytes.Buffer writes:

func NewBuffer(buf []byte) *Buffer
    func ExampleBuffer()
    func ExampleBuffer_reader()
func (b *Buffer) Bytes() []byte
   func ExampleBuffer_Bytes()
...

Note that (not shown) methods or other things with no examples still get printed too, showing the full API and that they have no examples.

(2) If you say go doc bytes.ExampleBuffer (where ExampleBuffer is an actual example function), that prints the example source code along with any comment. This would mean go doc would have to load *_test.go files, both for the -ex flag and when the symbol name requested begins with Example.

@DeedleFake
Copy link
Author

What about the probably rare but technically possible case where a regular function that's not in a _test.go file starts with Example, and possibly even has the same name as an actual example? Default to regular one and then show example if -ex is given, maybe?

@robpike
Copy link
Contributor

robpike commented Jan 11, 2023

@rsc So with no -ex flag, the command behaves as before? I like that, but one of the desires was to find a way to express the existence of examples in that case. However, I am leaning towards thinking that sounds good in theory but won't work well in practice.

@rsc
Copy link
Contributor

rsc commented Jan 18, 2023

@robpike, yes, exactly.

@DeedleFake you're right, that can happen, even the conflict if the second one is in a package foo_test file.
I don't think it's going to happen but I suppose we could say -ex -src forces the use of the example.
By default go doc -src should continue to behave as it does today, which is to say it should print the non-test function.

Does anyone object to the proposal from last week? #26715 (comment)

@nickjwhite
Copy link

Sorry for taking so long to look at and respond to this. I like the above proposal, @rsc. To be clear, then, -ex in this proposal only affects whether examples are listed as existing for the symbol, and isn't needed to list the actual examples, as you'd just use e.g. go doc bytes.ExampleBuffer. Is that correct?

Your example output seems slightly incorrect to me, as ExampleBuffer() and ExampleBuffer_reader() are examples of Buffer, not NewBuffer(), so it would look more like this:

func ExampleBuffer()
func ExampleBuffer_reader()
func NewBuffer(buf []byte) *Buffer
func (b *Buffer) Bytes() []byte
   func ExampleBuffer_Bytes()
...

The lack of indentation to mark out these symbol-level examples is not ideal, as it doesn't clearly differentiate them as not part of the package API, but then starting the function list with indented Example functions would look odd too (though it would be in line with the preceding descriptive text). I'm unsure on which option would be preferable.

@rsc
Copy link
Contributor

rsc commented Jan 25, 2023

@nickjwhite, yes, -ex only controls printing the examples in the usual list.

@rsc
Copy link
Contributor

rsc commented Jan 25, 2023

Based on the discussion above, this proposal seems like a likely accept.
— rsc for the proposal review group

@rsc rsc moved this from Active to Likely Accept in Proposals Jan 25, 2023
@rsc
Copy link
Contributor

rsc commented Feb 1, 2023

No change in consensus, so accepted. 🎉
This issue now tracks the work of implementing the proposal.
— rsc for the proposal review group

@rsc rsc moved this from Likely Accept to Accepted in Proposals Feb 1, 2023
@rsc rsc changed the title proposal: cmd/doc: add example support cmd/doc: add example support Feb 1, 2023
@beoran
Copy link

beoran commented Feb 1, 2023

A question. Does -all also imply -ex?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
FeatureRequest Issues asking for a new feature that does not need a proposal. NeedsFix The path to resolution is known, but the work has not been done. Proposal-Accepted
Projects
Status: Accepted
Development

No branches or pull requests

8 participants