Documenter.jl: How to avoid repetitions of shared docstrings?

Suppose I have two functions that share a common docstring:

f() = 1
g() = 2

"docstring for `f` and `g`"
f, g

I only want to the docstring to appear once in the documentation, not twice. However, if I only write

```@docs
f
```

somewhere in docs/src, then I get an error,

These are docstrings in the checked modules (configured with the modules keyword)
that are not included in canonical @docs or @autodocs blocks.

Strictly speaking, the message is wrong because the docstring is included. Anyway, with warnonly I can turn the error message into a warning.

However, if I have a couple of such docstrings, then I get a long list of warnings that is hard to keep track of. As a result, I won’t notice anymore if there is some docstring indeed missing from the documentation.

I guess I could create an additional file in the docs/src directory where I list the omitted functions. But then I have to remember not to put that file up on the web site. I also have to manually create the page structure for the documentation for otherwise the new file will appear in the navigation bar.

Does anybody know a better solution?

1 Like

Why is it a problem that both docstrings appear on the documentation? After all, if the user looks for the docstring of g, it’s better that they find it right away?
If it is about space, you can collapse docstrings by default so that they don’t take too much room.

2 Likes

I’m not thinking of a user who looks up a specific docstring (which I would rather do in the REPL), but of someone who reads a good part of the documentation to learn about the package. As a user, I would find it quite annoying to be repeatedly confronted with the same text. I would also find it annoying to click all the time to open collapsed parts.

It’d probably be good to find a mainstream example of this. I really can’t recall multiple-expressions docstrings, even the mutating/nonmutating versions suggestion isn’t done in practice because the arguments are often different enough to warrant explanation. Your f and g examples are also different enough to warrant separate docstrings. Enums have a type for the shared part of the documentation, and the members usually reference that after specific information.

Something that has a similar effect though are const aliases for definitions (and not other things it seems). The aliases don’t get their own docstring in the source code so they don’t show up in references either, but help mode and @doc both retrieve the definition’s docstring via any alias.

I started doing that regularly, that there is one doc string mentioning both signatures, see e.g.

That way asking for either of the docs in REPL one gets aware of the other method easily, both compute the same anyways and the last line explains the in-place variable (in the JuliaManifolds ecosystem quite often the second variable).
In the docs we even define a string that is then attached to both methods.

Sure in there rendered docs that is a bit redundant, see e.g. the first two doc strings on Gradient Descent Β· Manopt.jl,
maybe one could collapse the second by default somewhen in Documenter, that would be neat.

Whether these two examples are mainstream, I leave to others to decide though.

1 Like

Here are two examples from SmallCollections.jl that I have in mind:

  • MapStyle – I find it more convenient to discuss it together with all four subtypes in a single entry.
  • any – I want to mention a common keyword argument added to some functions from Base. Again, I find it more convenient to present all four functions in one place.

Collapsing only some of the docstrings listed on a page (as suggested by @kellertuer) would be nice. Or listing the shared docstring with several functions in the headline, by saying somethig like

```@docs
f, g
```

But all these would be changes to Documenter.jl.

1 Like
"docstring for `f` and `g`"
f() = 1

"See [`f`](@ref)"
g() = 2
1 Like

This I think is pretty justifiable. Earlier I mentioned enums, and it’d also be nice sometimes if we could see the fixed number of instances and the type all described in one place without having to go through a reference.

Not a fan of how this involves several different functions. If I using SmallCollections then try help mode for the function or specific method for any, all, etc, I see method signatures for several functions I didn’t ask for. Also confused why I’m seeing Base’s docstring when I query the SmallBitSet method,

I thought method-wise docstrings weren't supposed to show other methods' or the wider function's in help mode so that we aren't given irrelevant call examples.
julia> begin
       foo(::String)=1
       bar(::String)=1
       "foobarstring"
       foo(::String), bar(::String)

       "barint"
       bar(::Int)=2

       "bar"
       bar

       "foo"
       foo
       end
foo

help?> bar(::String)
  foobarstring

help?> bar(::Int)
  barint

help?> bar
search: bar Pair Char mark

  foobarstring

  ───────────────────────────────────────────────────────────────

  barint

  ───────────────────────────────────────────────────────────────

  bar

It appears that Base.exp === ManifoldsBase.exp, and the docstrings for those method signatures in circle_group_real.jl interpolate a shared string instead of using the multiple expressions to one docstring syntax. That should technically be the same docstring, but that may end up having some effect on how Documenter works or will work.

An alternative would be to have discursive docs pages where only one docstring from each category is displayed (a kind of tutorial), and then one exhaustive API reference where they are all listed. Documenter provides the option to add duplicated docstrings that way by specifying that only one can be canonical.

2 Likes

Sure, technically that is different, mainly because I was not aware of the multiple expressions thing; nevertheless, this was more about the effect, and that is in practice currently the same – though I like the f,g approach!

Even besides how Documenter internally tracks docstrings, I don’t know how docstring identity is actually intended to work. Equal Strings are identical by definition, but equal docstrings instantiated and attached separately to different expressions are treated and displayed as different. But does the multiple expressions syntax imply a shared docstring or multiple duplicate docstrings as the documented β€œequivalent” separate expressions do?

I guess this is because of the type parameter in the SmallBitSet methods, see the PR below. I would say that docstring search by signature is broken in the presence of type parameters. I haven’t got any reaction from the developers so far.

1 Like