I am working on a package that will be used by a small team, so I am trying to use Documenter.jl in conjunction with DocStringExtensions.jl to generate the documentation while writing an decorating functions – as one should.
However, I would like to include LaTeX in my docstrings, for example the equation for a specific system that is handled by a generic function. Yet, I’d highly prefer to not escape many characters (like \\times, etc.) when the equations grow in size.
One way is to include the @doc raw """ ... prefix, which makes these escape sequences not needed anymore, but these also make DocStringExtensions essentially useless as the $(SIGNATURES) or $(TYPEDSIGNATURES) now do not work as these are interepreted as raw strings.
For example take this docstring for some function:
@doc raw"""
$(TYPEDSIGNATURES)
A function with some arguments, and an equation
```math
f(x) = \int_0^\infty \frac{dx}{1+x^2}
```
# Arguments
- `x::Float64`: x
- `y::Float64`: y
"""
function f(x::Float64, y::Float64)
nothing
end
When compiling the documentation, simply $(TYPEDSIGNATURES) is printed, instead of what is supposed to come out of it.
Is there any way to both include LaTeX syntax without excessive escaping, or is there a better way to “automatically” include function arguments in the docstring without having to type them manually? If not, is there any good argument for typing them manually? Or how should one approach documentation of (relatively) large packages with many functions?
Any help or tips is greatly appreciated. Thank you!
You could use a custom string macro instead of raw"...", similar to LaTeXStrings.jl which allows both unescaped \ and variable interpolation with %$:
julia> using LaTeXStrings
julia> foo = 7
7
julia> L"""
foo = %$foo
an equation $\sqrt{\frac{1}{1+x^2}}$.
"""
L"foo = 7
an equation $\sqrt{\frac{1}{1+x^2}}$.
"
e.g.
macro myL_str(s::String)
i = firstindex(s)
buf = IOBuffer(maxsize=ncodeunits(s))
ex = Expr(:call, :string)
while i <= ncodeunits(s)
c = @inbounds s[i]
i = nextind(s, i)
if c === '$' && i <= ncodeunits(s)
position(buf) > 0 && push!(ex.args, String(take!(buf)))
atom, i = Meta.parseatom(s, i, filename=string(__source__.file))
Meta.isexpr(atom, :incomplete) && error(atom.args[1])
atom !== nothing && push!(ex.args, atom)
continue
else
print(buf, c)
end
end
position(buf) > 0 && push!(ex.args, String(take!(buf)))
return esc(ex)
end
which should do what you want (supports interpolation with $ and supports unescaped backslashes), I think.
You still need @doc if you want to use a string macro (or anything other than an ordinary literal string) for documentation, I think.
And I would use @doc myL"..." foo rather than L"..." from LaTeXStrings, because you want the documentation string to be a String and not a LaTeXString. That’s why I said to use a macro similar to the one in LaTeXStrings.
Yeah sorry I was a bit too fast with my first reply. Still, using your macro and some equation in the string gives errors that things within the equation are not defined, e.g.,
@doc CustomL"""
These are some docs, with an inline equation $f(x) = 2x$.
"""
function doctest(x::Int)
nothing
end
gives rise to the error that
ERROR: LoadError: UndefVarError: `f` not defined
I am not super familiar with macros and this kind of string-manipulation, so I am not fully sure what your macro is trying to achieve. I also cannot believe I am the only one with this problem? Or do people generally not use DocStringExtensions.jl and just type function signatures manually?
Yes, if you want to use the deprecated $ as an equation delimiter (rather than ``, which is encouraged), then you will need to use something else for interpolation. e.g. LaTeXStrings uses %$ for interpolation, and you can copy the code for that if you want (just change the first line of the L_str macro to call string instead of latexstring).
Ah that is great, was not aware that I could use `` instead of $ to indicate math, which is indeed much clearer. Thanks a lot. I will try to understand the macro a bit better and add it to my codebase.