Skip to content

Commit 3fbdaf2

Browse files
authored
fixes for skip dir and adjustment to the docs (#169)
1 parent 3900afe commit 3fbdaf2

File tree

2 files changed

+132
-75
lines changed

2 files changed

+132
-75
lines changed

docs/src/man/ls+lit.md

Lines changed: 119 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -7,96 +7,160 @@ You've likely already seen how LiveServer could be used along with Documenter to
77
It is also easy to use LiveServer with both Documenter and [Literate.jl](https://github.com/fredrikekre/Literate.jl), a package for literate programming written by Fredrik Ekre that can convert julia script files into markdown.
88
This can be particularly convenient for documentation pages with a lot of code examples.
99

10-
Only two steps are required to have this working (assuming you have already added Literate to your environment):
10+
There are mainly two steps
1111

12-
1. pick a folder structure
13-
1. modify the `docs/make.jl` file to contain a line calling Literate
12+
1. have the `make.jl` file process the literate files to go from `.jl` to `.md` files,
13+
2. call `servedocs` with appropriate keywords.
1414

15-
### Folder structure
16-
17-
There are effectively two recommended ways, pick whichever one you prefer.
18-
In the first case, the script files `.jl` to be compiled by Literate are at the _same location_ as the output file so that you would have:
15+
The function `LiveServer.servedocs_literate_example` generates a directory which has the right structure that you can copy for your package.
16+
To experiment, do:
1917

20-
```
21-
docs
22-
└── src
23-
├── index.jl
24-
└── index.md
18+
```julia-repl
19+
julia> using LiveServer
20+
julia> LiveServer.servedocs_literate_example("test_dir")
21+
julia> cd("test_dir")
22+
julia> servedocs(literate_dir=joinpath("docs", "literate"))
2523
```
2624

27-
if you're happy with this, then you can jump to the [next step](#Modifying-the-make-file-1) to change the make file.
25+
if you then navigate to `localhost:8000` you should end up with
2826

29-
However you may not be happy with this, in particular if you have lots of such files and a mix of files which are generated by `Literate` and some which aren't, then typically you might prefer to keep all scripts in a separate folder.
30-
You would just have to make sure that the output is properly redirected to `docs/src`.
31-
Your folder structure would then look something like:
27+
![](/assets/testlit.png)
3228

33-
```
34-
docs
35-
├── lit
36-
│   └── index.jl
37-
└── src
38-
└── index.md
39-
```
29+
if you modify `test_dir/docs/literate/man/pg1.jl` for instance writing `f(4)` it will be applied directly:
4030

41-
The only thing you have to do in this case is to specify to `servedocs` where the "literate folder" is; this is a keyword argument and for the example above we would have:
31+
![](/assets/testlit2.png)
32+
33+
34+
In the explanations below we assume you have defined
35+
36+
* `LITERATE_INPUT` the directory where the literate files are,
37+
* `LITERATE_OUTPUT` the directory where the generated markdown files will be.
4238

43-
```julia
44-
servedocs(literate_dir=joinpath("docs", "lit"))
45-
```
4639

47-
### Modifying the make file
40+
## Having the make file call Literate
4841

49-
The only thing you have to do here is add a few lines to specify which files should be compiled by `Literate`.
50-
Assuming you have taken the second path in the situation above, your `make.jl` file should look like:
42+
Here's a basic `make.jl` file which loops over the files in `LITERATE_INPUT` to generate files
43+
in `LITERATE_OUTPUT`
5144

5245
```julia
5346
using Documenter, Literate
5447

55-
src = joinpath(@__DIR__, "src")
56-
lit = joinpath(@__DIR__, "lit")
48+
LITERATE_INPUT = ...
49+
LITERATE_OUTPUT = ...
5750

58-
for (root, _, files) walkdir(lit), file files
51+
for (root, _, files) walkdir(LITERATE_INPUT), file files
52+
# ignore non julia files
5953
splitext(file)[2] == ".jl" || continue
54+
# full path to a literate script
6055
ipath = joinpath(root, file)
61-
opath = splitdir(replace(ipath, lit=>src))[1]
56+
# generated output path
57+
opath = splitdir(replace(ipath, LITERATE_INPUT=>LITERATE_OUTPUT))[1]
58+
# generate the markdown file calling Literate
6259
Literate.markdown(ipath, opath)
6360
end
6461

6562
makedocs(
66-
sitename = "Test",
67-
modules = [Test],
68-
pages = ["Home" => "index.md"]
63+
...
6964
)
7065
```
7166

72-
If you were happy with the `.jl` and `.md` files being in the same location, simply replace the `lit = ` line by
67+
## Calling servedocs with the right arguments
68+
69+
`LiveServer.servedocs` needs to know two things to work with literate scripts properly:
70+
71+
* where the scripts are
72+
* where the generated files will be
73+
74+
it can make assumptions for some basic cases but, in general, you'll have to provide both.
75+
76+
Doing so improperly may lead to an infinite loop where:
77+
* the first `make.jl` call generates markdown files with Literate
78+
* these generated markdown files themselves trigger `make.jl`
79+
* (infinite loop)
80+
81+
To avoid this, you must generally call `servedocs` as follows when working with literate files:
82+
83+
```
84+
servedocs(
85+
literate_dir = LITERATE_INPUT
86+
skip_dir = LITERATE_OUTPUT
87+
)
88+
```
89+
90+
where
91+
92+
* `literate_dir` is the parent directory of the literate scripts, and
93+
* `skip_dir` is the parent directory where the generated markdown files are placed.
94+
95+
**Special cases**:
96+
97+
* if the literate scripts are located in `docs/src` you can just specify `literate_dir=""`,
98+
* if the literate scripts are generated with in `docs/src` with the exact same relative path, you
99+
do not need to specify `skip_dir`.
100+
101+
102+
### Example 1 <!-- checked 10/7/2023 | 1.2.1 -->
103+
104+
```
105+
docs
106+
└── src
107+
├── literate_script.jl
108+
└── literate_script.md
109+
```
110+
111+
in this case we can call
73112

74113
```julia
75-
lit = src
114+
servedocs(literate="")
76115
```
77116

78-
What the for loop does is simple: it loops over the files in the folder where it's likely to encounter `.jl` files and for those it encounters:
117+
since
79118

80-
1. it retrieves the path to the file (`ipath`)
81-
1. it constructs the output path in `docs/src` (`opath`)
82-
1. it compiles the file `ipath` and saves the output at `opath`
119+
1. the literate scripts are under `docs/src`
120+
2. the generated markdown files have exactly the same relative path.
83121

84-
## Complete example
85122

86-
The function `LiveServer.servedocs_literate_example` generates a directory which has the right structure that you can copy for your package.
87-
To experiment, do:
123+
### Example 2 <!-- checked 10/7/2023 | 1.2.1 -->
88124

89-
```julia-repl
90-
julia> using LiveServer
91-
julia> LiveServer.servedocs_literate_example("test_dir")
92-
julia> cd("test_dir")
93-
julia> servedocs(literate_dir=joinpath("docs", "literate"))
125+
```
126+
docs
127+
├── literate
128+
│ └── literate_script.jl
129+
└── src
130+
└── literate_script.md
94131
```
95132

96-
if you then navigate to `localhost:8000` you should end up with
133+
in this case we can call
97134

98-
![](/assets/testlit.png)
135+
```julia
136+
servedocs(literate=joinpath("docs", "literate"))
137+
```
99138

100-
if you modify `test_dir/docs/literate/man/pg1.jl` for instance writing `f(4)` it will be applied directly:
139+
since
140+
141+
1. the literate scripts are under a dedicated folder,
142+
2. the generated markdown files have exactly the same relative path.
143+
144+
### Example 3 <!-- checked 10/7/2023 | 1.2.1 -->
145+
146+
```
147+
foo
148+
├── literate
149+
│ └── literate_script.jl
150+
docs
151+
└── src
152+
└── generated
153+
└── literate_script.md
154+
```
155+
156+
in this case we can call
157+
158+
```julia
159+
servedocs(literate=joinpath("foo", "literate"), skip_dir=joinpath())
160+
```
161+
162+
since
163+
164+
1. the literate scripts are under a dedicated folder,
165+
2. the generated markdown files do not have exactly the same relative path.
101166

102-
![](/assets/testlit2.png)

src/utils.jl

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -180,24 +180,13 @@ subfolder `docs`.
180180
* `doc_env=false`: a boolean switch to make the server start by activating the
181181
doc environment or not (i.e. the `Project.toml` in `docs/`).
182182
* `literate=nothing`: see `literate_dir`.
183-
* `literate_dir=nothing`: Path to a directory containing Literate scripts. If
184-
`nothing`, it's assumed there are no such scripts.
185-
Any `*.jl` file in the folder (or subfolders) is
186-
checked for changes and is assumed to generate a
187-
`*.md` file with the same name, in the same location.
188-
It is necessary to indicate this path to avoid a
189-
recursive trigger loop where the generated `*.md` file
190-
triggers, causing the literate script to be
191-
re-evaluated which, in turn, re-generates the `*.md`
192-
file.
193-
If the generated `*.md` file are in fact not located
194-
in the same location as their source `*.jl` file,
195-
then the user must indicate that these `*.md` files
196-
should be ignored (should not trigger) by using
197-
`skip_dir` or `skip_files`.
198-
* `skip_dir=""`: a subpath of `docs/` where modifications should not trigger
199-
the generation of the docs, this is useful for instance if
200-
you're using Weave and Weave generates some files in
183+
* `literate_dir=nothing`: Path to a directory containing Literate scripts if
184+
these are not simply under `docs/src`.
185+
See also the docs for information about how to
186+
work with Literate.
187+
* `skip_dir=""`: a path starting with `docs/` where modifications should not
188+
trigger the generation of the docs, this is useful for instance
189+
if you're using Weave and Weave generates some files in
201190
`docs/src/examples` in which case you should set
202191
`skip_dir=joinpath("docs","src","examples")`.
203192
* `skip_dirs=[]`: same as `skip_dir` but for a list of such dirs. Takes
@@ -239,6 +228,7 @@ function servedocs(;
239228
if isempty(skip_dirs) && !isempty(skip_dir)
240229
skip_dirs = [skip_dir]
241230
end
231+
push!(skip_dirs, joinpath("docs", "build"))
242232
skip_dirs = abspath.(skip_dirs)
243233
skip_files = abspath.(skip_files)
244234
include_dirs = abspath.(include_dirs)
@@ -248,16 +238,19 @@ function servedocs(;
248238
if isnothing(literate_dir) && !isnothing(literate)
249239
literate_dir = literate
250240
end
241+
if !isnothing(literate_dir)
242+
literate_dir = abspath(literate_dir)
243+
end
251244

252-
path2makejl = joinpath(foldername, makejl)
245+
path2makejl = joinpath(foldername, makejl)
253246

254247
# The file watcher is a default SimpleWatcher with a custom
255248
# callback
256249
docwatcher = SimpleWatcher()
257250
set_callback!(
258251
docwatcher,
259252
fp -> servedocs_callback!(
260-
docwatcher, fp, path2makejl,
253+
docwatcher, abspath(fp), path2makejl,
261254
literate_dir,
262255
skip_dirs, skip_files, include_dirs, include_files,
263256
foldername, buildfoldername

0 commit comments

Comments
 (0)