LiveServer + Literate
(Thanks to Fredrik Ekre and Benoit Pasquier for their input; a lot of this section is drawn from an early prototype suggested by Fredrik.)
You've likely already seen how LiveServer could be used along with Documenter to have live updating documentation (see servedocs
if not).
It is also easy to use LiveServer with both Documenter and Literate.jl, a package for literate programming written by Fredrik Ekre that can convert julia script files into markdown. This can be particularly convenient for documentation pages with a lot of code examples.
There are mainly two steps
- have the
make.jl
file process the literate files to go from.jl
to.md
files, - call
servedocs
with appropriate keywords.
The function LiveServer.servedocs_literate_example
generates a directory which has the right structure that you can copy for your package. To experiment, do:
julia> using LiveServer
julia> LiveServer.servedocs_literate_example("test_dir")
julia> cd("test_dir")
julia> servedocs(literate_dir=joinpath("docs", "literate"))
if you then navigate to localhost:8000
you should end up with
if you modify test_dir/docs/literate/man/pg1.jl
for instance writing f(4)
it will be applied directly:
In the explanations below we assume you have defined
LITERATE_INPUT
the directory where the literate files are,LITERATE_OUTPUT
the directory where the generated markdown files will be.
Having the make file call Literate
Here's a basic make.jl
file which loops over the files in LITERATE_INPUT
to generate files in LITERATE_OUTPUT
using Documenter, Literate
LITERATE_INPUT = ...
LITERATE_OUTPUT = ...
for (root, _, files) ∈ walkdir(LITERATE_INPUT), file ∈ files
# ignore non julia files
splitext(file)[2] == ".jl" || continue
# full path to a literate script
ipath = joinpath(root, file)
# generated output path
opath = splitdir(replace(ipath, LITERATE_INPUT=>LITERATE_OUTPUT))[1]
# generate the markdown file calling Literate
Literate.markdown(ipath, opath)
end
makedocs(
...
)
Calling servedocs with the right arguments
LiveServer.servedocs
needs to know two things to work with literate scripts properly:
- where the scripts are
- where the generated files will be
it can make assumptions for some basic cases but, in general, you'll have to provide both.
Doing so improperly may lead to an infinite loop where:
- the first
make.jl
call generates markdown files with Literate - these generated markdown files themselves trigger
make.jl
- (infinite loop)
To avoid this, you must generally call servedocs
as follows when working with literate files:
servedocs(
literate_dir = LITERATE_INPUT,
skip_dir = LITERATE_OUTPUT
)
where
literate_dir
is the parent directory of the literate scripts, andskip_dir
is the parent directory where the generated markdown files are placed.
Special cases:
- if the literate scripts are located in
docs/src
you can just specifyliterate_dir=""
, - if the literate scripts are generated with in
docs/src
with the exact same relative path, you do not need to specifyskip_dir
.
Examples
In the examples below, the file literate_script.jl
represents a Literate script and literate_script.md
represents the markdown file generated by Literate. What matters here is where these files are with respect to one another and with respect to docs/src
.
Example 1
docs
└── src
├── literate_script.jl
└── literate_script.md
in this case we can simply call
servedocs(literate="")
since
- the literate scripts are under
docs/src
(and so are watched by default), - the generated markdown files have exactly the same relative path (so there's no need to specify where the literate files are placed, this is implicit).
Example 2
docs
├── literate
│ └── literate_script.jl
└── src
└── literate_script.md
in this case we can call
servedocs(
literate=joinpath("docs", "literate")
)
since
- the literate scripts are under a dedicated folder that is not under
docs/src
and so must also be watched for changes, - the generated markdown files have exactly the same relative path.
Example 3
foo
├── literate
│ └── literate_script.jl
docs
└── src
└── generated
└── literate_script.md
in this case we can call
servedocs(
literate=joinpath("foo", "literate"),
skip_dir=joinpath("docs", "src", "generated")
)
since
- the literate scripts are under a dedicated folder,
- the generated markdown files do not have exactly the same relative path (since there is the added
generated
in the path).