What are good ways to structure reoccurring phrases in doc-strings

I recently refactored my doc strings after this discussion. In that thread already, @jules wrote, that in Makie they splice a lot of doc strings.

I started doing something like that as well collecting phrases I often use, so I can reuse them, have to type them less often (make less typos) and can if necessary change some defaults also in the docs more globally. I also started to unify my LaTeX notation a bit, since I have much more non-raw strings now in order to interpolate.

I do not like my current approach with a loose bunch of Strings. I will change that probably to a dictionary (storing variable names, their defaults, descriptions, etc).

To add an example: A lot of my methods have a parameter M the manifold. So, since I also like to add math, a lot of my docs have the line

* `M`: a Riemannian manifold ``\mathcal M``
  • sometimes M is even a keyword and has a nice default
  • if I write manifold the umptenth time, it comes out as manifodl
  • Maybe sometime in the future I want to change that (often occuring) snipped for some reason.

so:

Are there other people around doing something like that? Would it maybe be useful coordinating this into something like a DocumenterGlossary approach? Would that be useful or a waste of time?

3 Likes

Here is a first idea I am currently trying on a PR (cf Manopt.jl/src/documentation_glossary.jl at kellertuer/rework-high-level-interfaces ยท JuliaManifolds/Manopt.jl ยท GitHub โ€“ please ignore anything below line 213, those are old strings I used before and have not yet replaced)

internally this uses a dictionary of

  • Strings โ€“ when the snipped is really just a string without any parameters
  • Functions โ€“ usually with keyword arguments to format a string like (; M="M") -> "represents the manifold ``$(_tex(:Cal, M))``"
  • further sub dictionaries.

The whole system uses a package local variable _manopt_glossary for now, but that is for a single package maybe a reasonable start.
There is a function define! to add entries to the whole glossary, which is meant to be grouped, to for example

define!(
    :Variable, :p, :description, (; M="M") -> "a point on the manifold ``$(_tex(:Cal, M))``"
)
define!(:Variable, :p, :type, "P")
define!(:Variable, :p, :default, (; M="M") -> _link(:rand; M=M))

as well as _var(args...; kwargs...) = glossary(:Variables, args...; kwargs...) (similarly _links maps to the :Links dictionary and rand is actually a slightly complicated @extref link here).

The same way glossary(:Variable, :p, :description) would just print the description, with glossary(:Variable, :p, :description; M="N") one can change the calligraphic letter in the string.

And one can of course combine these 3 fields of p to a nice group meta entries like

define!(
    :Variable,
    :Argument,
    (s::Symbol, display="$s"; type=false, kwargs...) ->
        "* `$(display)$(type ? "::$(_var(s, :type))" : "")`: $(_var(s, :description;kwargs...))",
)
define!(
    :Variable,
    :Field,
    (s::Symbol, display="$s"; kwargs...) ->
        "* `$(display)::$(_var(s, :type))`: $(_var(s, :description; kwargs...))",
)

one gets an

  • argument style display with _var(:Argument, :p) as "* `p`: a point on the manifold ``\\mathcal M``"
  • a field style display with _var(:Field, :p) as "* `p::P`: a point on the manifold ``\\mathcal M``"

ok for now these only differ in the sense that the field display also shows the type.
Similarly one can default keyword display that also would show the default defined above.

I would say this is still a bit hacked, but makes it already nice to handle (or group) snippets in a unified way.
I think I will experiment with this and use it within my package. If someone is interested in a maybe better structure as a standalone package, I would also take that in to account. One could maybe also define that in some YAML or so.