Skip to content

Commit 0cc5a4c

Browse files
authored
Merge pull request #397 from control-toolbox/doc
Update documentation
2 parents 623c195 + 496b449 commit 0cc5a4c

File tree

11 files changed

+814
-133
lines changed

11 files changed

+814
-133
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "CTBase"
22
uuid = "54762871-cc72-4466-b8e8-f6c8b58076cd"
3-
version = "0.17.3"
3+
version = "0.17.4"
44
authors = ["Olivier Cots <[email protected]>", "Jean-Baptiste Caillau <[email protected]>"]
55

66
[deps]

docs/Project.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
[deps]
2+
CTBase = "54762871-cc72-4466-b8e8-f6c8b58076cd"
23
Coverage = "a2441757-f6aa-5fb2-8edb-039e3f45d037"
34
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
45
MarkdownAST = "d0879d2d-cac2-40c8-9cee-1863dc0c7391"

docs/make.jl

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,14 @@ with_api_reference(src_dir) do api_pages
6565
asset("https://control-toolbox.org/assets/js/documentation.js"),
6666
],
6767
),
68-
pages=["Introduction" => "index.md", "API Reference" => api_pages],
68+
pages=[
69+
"Introduction" => "index.md",
70+
"Developers Guide" => [
71+
"Testing and Coverage" => "test-coverage-guide.md",
72+
"Documentation" => "documentation-guide.md",
73+
],
74+
"API Reference" => api_pages,
75+
],
6976
checkdocs=:none,
7077
)
7178
end

docs/src/documentation-guide.md

Lines changed: 322 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,322 @@
1+
# Documentation Guide
2+
3+
This guide explains how to set up automated API reference documentation generation using the `DocumenterReference` extension. This is particularly useful for maintaining comprehensive and up-to-date API documentation as your codebase evolves.
4+
5+
## Overview
6+
7+
The `DocumenterReference` extension provides the `CTBase.automatic_reference_documentation()` function, which automatically generates API reference pages from your Julia source code. It:
8+
9+
- Extracts docstrings from your modules
10+
- Separates public and private APIs
11+
- Generates markdown files suitable for Documenter.jl
12+
- Handles extensions and optional dependencies gracefully
13+
- Supports filtering and customization
14+
15+
## Architecture
16+
17+
### Directory Structure
18+
19+
```text
20+
docs/
21+
├── make.jl # Main documentation build script
22+
├── api_reference.jl # API reference generation logic
23+
└── src/
24+
├── index.md # Documentation homepage
25+
├── developers-guide.md # Testing and coverage guide
26+
└── documentation-guide.md # This file
27+
```
28+
29+
### How It Works
30+
31+
The documentation generation happens in two stages:
32+
33+
1. **`api_reference.jl`**: Defines `generate_api_reference()` which calls `CTBase.automatic_reference_documentation()` for each module
34+
2. **`make.jl`**: Calls `with_api_reference()` which executes the generation and passes the pages to `Documenter.makedocs()`
35+
36+
## Setting Up API Documentation
37+
38+
### Basic Configuration
39+
40+
The core function is `CTBase.automatic_reference_documentation()`. Here's a minimal example:
41+
42+
```julia
43+
using CTBase
44+
using Documenter
45+
46+
CTBase.automatic_reference_documentation(;
47+
subdirectory=".",
48+
primary_modules=[MyModule => ["src/MyModule.jl"]],
49+
title="MyModule API",
50+
title_in_menu="API",
51+
filename="api",
52+
)
53+
```
54+
55+
### Key Parameters
56+
57+
- **`subdirectory`**: Where to write generated markdown files (relative to `docs/src`)
58+
- **`primary_modules`**: Vector of modules to document, optionally with source files
59+
- Format: `Module` or `Module => ["path/to/file.jl"]`
60+
- When source files are provided, only symbols from those files are documented
61+
- **`title`**: Title displayed at the top of the generated page
62+
- **`title_in_menu`**: Title in the navigation menu (defaults to `title`)
63+
- **`filename`**: Base filename for the markdown file (without `.md` extension)
64+
- **`exclude`**: Vector of symbol names to skip from documentation
65+
- **`public`**: Generate public API page (default: `true`)
66+
- **`private`**: Generate private API page (default: `true`)
67+
- **`external_modules_to_document`**: Additional modules to search for docstrings (e.g., `[Base]`)
68+
69+
### Public vs. Private API
70+
71+
The `public` and `private` flags control which symbols are documented:
72+
73+
#### Option 1: Public API Only (`public=true, private=false`)
74+
75+
```julia
76+
CTBase.automatic_reference_documentation(;
77+
subdirectory=".",
78+
primary_modules=[MyModule => src("MyModule.jl")],
79+
public=true,
80+
private=false,
81+
title="MyModule API",
82+
filename="api",
83+
)
84+
```
85+
86+
**Result**: Only exported symbols are documented. This is ideal for end-user documentation.
87+
88+
#### Option 2: Private API Only (`public=false, private=true`)
89+
90+
```julia
91+
CTBase.automatic_reference_documentation(;
92+
subdirectory=".",
93+
primary_modules=[MyModule => src("MyModule.jl")],
94+
public=false,
95+
private=true,
96+
title="MyModule Internals",
97+
filename="internals",
98+
)
99+
```
100+
101+
**Result**: Only non-exported (private) symbols are documented. Useful for developer documentation.
102+
103+
#### Option 3: Both Public and Private (`public=true, private=true`)
104+
105+
```julia
106+
CTBase.automatic_reference_documentation(;
107+
subdirectory=".",
108+
primary_modules=[MyModule => src("MyModule.jl")],
109+
public=true,
110+
private=true,
111+
title="MyModule Complete Reference",
112+
filename="complete_api",
113+
)
114+
```
115+
116+
**Result**: All symbols are documented in a single page. This provides a comprehensive reference.
117+
118+
### Example: CTBase Configuration
119+
120+
Here's how CTBase configures its API documentation in `docs/api_reference.jl`:
121+
122+
```julia
123+
function generate_api_reference(src_dir::String)
124+
# Helper functions to build absolute paths
125+
src(files...) = [abspath(joinpath(src_dir, f)) for f in files]
126+
ext_dir = abspath(joinpath(src_dir, "..", "ext"))
127+
ext(files...) = [abspath(joinpath(ext_dir, f)) for f in files]
128+
129+
# Symbols to exclude from all API pages
130+
EXCLUDE_SYMBOLS = Symbol[:include, :eval]
131+
132+
pages = [
133+
# Main CTBase module - private API only
134+
CTBase.automatic_reference_documentation(;
135+
subdirectory=".",
136+
primary_modules=[CTBase => src("CTBase.jl")],
137+
exclude=EXCLUDE_SYMBOLS,
138+
public=false,
139+
private=true,
140+
title="CTBase",
141+
title_in_menu="CTBase",
142+
filename="ctbase",
143+
),
144+
# Other modules...
145+
]
146+
147+
# Extensions are checked with Base.get_extension
148+
DocumenterReference = Base.get_extension(CTBase, :DocumenterReference)
149+
if !isnothing(DocumenterReference)
150+
push!(
151+
pages,
152+
CTBase.automatic_reference_documentation(;
153+
subdirectory=".",
154+
primary_modules=[DocumenterReference => ext("DocumenterReference.jl")],
155+
external_modules_to_document=[CTBase],
156+
exclude=EXCLUDE_SYMBOLS,
157+
public=false,
158+
private=true,
159+
title="DocumenterReference",
160+
title_in_menu="DocumenterReference",
161+
filename="documenter_reference",
162+
),
163+
)
164+
end
165+
166+
return pages
167+
end
168+
```
169+
170+
## Handling Extensions
171+
172+
When your package uses extensions (weak dependencies), you need to check if they're loaded before documenting them:
173+
174+
```julia
175+
# Check if the extension is loaded
176+
MyExtension = Base.get_extension(MyPackage, :MyExtension)
177+
if !isnothing(MyExtension)
178+
push!(
179+
pages,
180+
CTBase.automatic_reference_documentation(;
181+
subdirectory=".",
182+
primary_modules=[MyExtension => ext("MyExtension.jl")],
183+
external_modules_to_document=[MyPackage],
184+
exclude=EXCLUDE_SYMBOLS,
185+
public=false,
186+
private=true,
187+
title="MyExtension",
188+
title_in_menu="MyExtension",
189+
filename="my_extension",
190+
),
191+
)
192+
end
193+
```
194+
195+
This ensures that:
196+
197+
- Documentation is only generated if the extension is actually loaded
198+
- The extension can reference types and functions from the main package via `external_modules_to_document`
199+
200+
## Integration with Documenter.jl
201+
202+
In `docs/make.jl`, use `with_api_reference()` to integrate the generated pages:
203+
204+
```julia
205+
using Documenter
206+
using CTBase
207+
208+
include("api_reference.jl")
209+
210+
with_api_reference(dirname(@__DIR__)) do api_pages
211+
makedocs(;
212+
modules=[CTBase],
213+
authors="Your Name",
214+
repo="https://github.com/yourname/yourpackage.jl",
215+
sitename="YourPackage.jl",
216+
format=Documenter.HTML(;
217+
assets=String[],
218+
),
219+
pages=[
220+
"Introduction" => "index.md",
221+
"Developers Guide" => "developers-guide.md",
222+
"Documentation Guide" => "documentation-guide.md",
223+
"API Reference" => api_pages,
224+
],
225+
checkdocs=:none,
226+
)
227+
end
228+
```
229+
230+
The `with_api_reference()` function:
231+
232+
1. Generates the API reference pages
233+
2. Passes them to your `makedocs()` call
234+
3. Cleans up temporary generated files after the build
235+
236+
## DocType System
237+
238+
The `DocumenterReference` extension recognizes several documentation element types:
239+
240+
- **`DOCTYPE_ABSTRACT_TYPE`**: Abstract type declarations
241+
- **`DOCTYPE_STRUCT`**: Concrete struct types
242+
- **`DOCTYPE_FUNCTION`**: Functions and callables
243+
- **`DOCTYPE_MACRO`**: Macros (names starting with `@`)
244+
- **`DOCTYPE_MODULE`**: Submodules
245+
- **`DOCTYPE_CONSTANT`**: Constants and non-function values
246+
247+
These types are automatically detected and organized in the generated documentation.
248+
249+
## Best Practices
250+
251+
1. **Exclude internal symbols**: Use the `exclude` parameter to hide implementation details
252+
253+
```julia
254+
exclude=Symbol[:_internal_helper, :_private_constant]
255+
```
256+
257+
2. **Separate public and private**: Create separate pages for public and private APIs
258+
259+
```julia
260+
# Public API
261+
CTBase.automatic_reference_documentation(;
262+
...,
263+
public=true,
264+
private=false,
265+
filename="api_public",
266+
)
267+
# Private API
268+
CTBase.automatic_reference_documentation(;
269+
...,
270+
public=false,
271+
private=true,
272+
filename="api_private",
273+
)
274+
```
275+
276+
3. **Document external modules**: Use `external_modules_to_document` to include methods from other packages
277+
278+
```julia
279+
CTBase.automatic_reference_documentation(;
280+
...,
281+
external_modules_to_document=[Base, Documenter],
282+
)
283+
```
284+
285+
4. **Check extensions before documenting**: Always use `Base.get_extension()` to safely check for optional dependencies
286+
287+
```julia
288+
MyExt = Base.get_extension(MyPackage, :MyExtension)
289+
if !isnothing(MyExt)
290+
# Document the extension
291+
end
292+
```
293+
294+
## Troubleshooting
295+
296+
### Missing Docstrings
297+
298+
If symbols appear without docstrings in the generated documentation, ensure:
299+
300+
- The docstring is defined immediately before the symbol
301+
- The docstring uses the correct Julia docstring syntax (triple quotes)
302+
- The symbol is actually exported or included in your module
303+
304+
### Symbols Not Appearing
305+
306+
If expected symbols don't appear in the documentation:
307+
308+
- Check if they're in the `exclude` list
309+
- Verify the source file path is correct
310+
- Ensure the symbol is defined in the specified source file (not imported)
311+
312+
### Extension Not Documented
313+
314+
If an extension's documentation isn't generated:
315+
316+
- Verify the extension is loaded with `Base.get_extension()`
317+
- Check that the extension file path is correct
318+
- Ensure the extension module is properly defined
319+
320+
## Summary
321+
322+
The `DocumenterReference` extension provides a powerful, flexible system for automatically generating API documentation. By following the patterns shown in this guide, you can maintain comprehensive, up-to-date documentation with minimal manual effort.

0 commit comments

Comments
 (0)