Documenting a plot recipe

ModelPredictiveControl.jl defines plot recipes that works on SimResult types. My recipes include keyword arguments like plotu=false so user can type

using ModelPredictiveControl, ControlSystemsBase, Plots
res = sim!(LinModel(tf(2, [10, 1]), 1.0), 50) # return a SimResult object
plot(res, plotu=false)

to plot the model output, without including an additional subplot with the manipulated input. I would like to document these keyword arguments. Ideally both in the REPL with ?-mode and in the online documentation. According to the RecipesBase.jl documentation:

Documenting plot functions

A documentation string added above the recipe definition will have no effect, just like the function name is meaningless. Since everything in Julia can be associated with a doc-string, the documentation can be added to the name of the plot function like this

"""
My docstring
"""
my_plotfunc

This can be put anywhere in the code and will appear on the call ?my_plotfunc.

This trick does work for the ?-mode, but for the Documenter.jl doc, this block:

```@docs
ModelPredictiveControl.my_plotfunc
```

produces this error at building:

┌ Error: undefined binding 'ModelPredictiveControl.my_plotfunc' in `@docs` block in src/public/plot_sim.md:30-32
│ ```@docs
│ ModelPredictiveControl.plot_recipe
│ ```
└ @ Documenter ~/.julia/packages/Documenter/C1XEF/src/utilities/utilities.jl:44

What’s the best way to do that ?

2 Likes

Anybody ? I think many package maintainers would benefit from this feature!

edit: maybe @mortenpi or @tbreloff has suggestions ? I feel like there is a big gap here. Many packages does not document enough their plot recipe, and it may be one of the cause.

2 Likes

If you make a “user plot” recipe, e.g.

@userplot MyPlot
@recipe function plot_recipe(mp::MyPlot)
    ...
end

then you can document the generated functions myplot and myplot! with something like

@doc raw"""
    myplot(t_end)
    myplot!(t_end)

Documentation here
"""
myplot
@doc (@doc myplot) myplot!

I have done this in my own package, and Documenter handles this just fine.

But my intuition is that part of the magic of RecipesBase is that Plots.plot doesn’t get defined within your package, which is a good thing, so generating documentation for plot(res::SimResults) is much more complicated, or maybe impossible.

Also note that, per the documentation of RecipesBase, the function name in your recipe (so plot_recipe in my above example) gets thrown away so don’t expect to document that:

The function name f in is irrelevant and can be replaced by any other function name. @recipe does not use it.

1 Like

Thanks for your answer!

Since I have 3 distinct methods for my recipe that dispatch on the parameter of the SimResult instance (the code is bit simplified for my explanation):

@recipe plot_recipe(res::SimResult{<:SimModel}; arg1=true, ...) = ...
@recipe plot_recipe(res::SimResult{<:StateEstimator}; arg2=true, ...) = ...
@recipe plot_recipe(res::SimResult{<:PredictiveController}; arg3=true, ...) = ...

it’s not clear how I can apply your suggestion.

I ended up creating 3 additional dummy methods for the sole purpose of documenting my plot recipes:

"My docstring 1"
plot_recipe(::Nothing, ::SimResult{<:SimModel}) = nothing
"My docstring 2"
plot_recipe(::Nothing, ::SimResult{<:StateEstimator}) = nothing
"My docstring 3"
plot_recipe(::Nothing, ::SimResult{<:PredictiveController}) = nothing

It’s not the cleanest solution since it’s hard to find in the ?-mode, but it works well in the online documentation.