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.jlfile process the literate files to go from.jlto.mdfiles, - call
servedocswith 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_INPUTthe directory where the literate files are,LITERATE_OUTPUTthe 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.jlcall 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_diris the parent directory of the literate scripts, andskip_diris the parent directory where the generated markdown files are placed.
Special cases:
- if the literate scripts are located in
docs/srcyou can just specifyliterate_dir="", - if the literate scripts are generated with in
docs/srcwith 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.mdin 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.mdin this case we can call
servedocs(
literate=joinpath("docs", "literate")
)since
- the literate scripts are under a dedicated folder that is not under
docs/srcand 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.mdin 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
generatedin the path).