CurrentModule = CTBase
This guide explains how to set up automated API reference documentation generation using the DocumenterReference extension of CTBase.jl. This is particularly useful for maintaining comprehensive and up-to-date API documentation as your codebase evolves.
The DocumenterReference extension provides the CTBase.automatic_reference_documentation function, which automatically generates API reference pages from your Julia source code. It:
- Extracts docstrings from your modules
- Separates public and private APIs
- Generates markdown files suitable for
Documenter.jl - Handles extensions and optional dependencies gracefully
- Supports filtering and customization
docs/
├── make.jl # Main documentation build script
├── api_reference.jl # API reference generation logic
└── src/
├── index.md # Documentation homepage
└── ...
The documentation generation happens in two stages:
api_reference.jl: Definesgenerate_api_reference()which callsCTBase.automatic_reference_documentation()for each module.make.jl: Callswith_api_reference()which executes the generation and passes the pages toDocumenter.makedocs().
The core function is CTBase.automatic_reference_documentation(). Here's a minimal example:
using CTBase
using Documenter
CTBase.automatic_reference_documentation(;
subdirectory=".",
primary_modules=[MyModule => ["src/MyModule.jl"]],
title="MyModule API",
title_in_menu="API",
filename="api",
)subdirectory: Where to write generated markdown files (relative todocs/src).primary_modules: Vector of modules to document, optionally with source files.- Format:
ModuleorModule => ["path/to/file.jl"]. - When source files are provided, only symbols from those files are documented.
- Format:
title: Title displayed at the top of the generated page.title_in_menu: Title in the navigation menu (defaults totitle).filename: Base filename for the markdown file (without.mdextension).exclude:Vector{Symbol}of symbol names to skip (e.g.,[:eval, :include]).public: Generate public API page (default:true).private: Generate private API page (default:true).sort_by: Custom sort function applied to symbol lists (default:identity).include_without_source: Iftrue, include symbols whose source file cannot be determined (default:false).source_files: (Deprecated) Global source file paths. Preferprimary_modules=[Module => files]instead.external_modules_to_document: Additional modules to search for docstrings (e.g.,[Base]).public_title: Custom title for public API page (empty string uses default).private_title: Custom title for private API page (empty string uses default).public_description: Custom description for public API page (empty string uses default).private_description: Custom description for private API page (empty string uses default).
The public and private flags control which symbols are documented:
CTBase.automatic_reference_documentation(;
# ...
public=true,
private=false,
title="MyModule API",
filename="api",
)Result: Only exported symbols are documented. This is ideal for end-user documentation.
CTBase.automatic_reference_documentation(;
# ...
public=false,
private=true,
title="MyModule Internals",
filename="internals",
)Result: Only non-exported (private) symbols are documented. Useful for developer documentation.
CTBase.automatic_reference_documentation(;
# ...
public=true,
private=true,
title="MyModule Complete Reference",
filename="complete_api",
)Result: If public and private are both true, the function returns a structure with two sub-pages (Public and Private).
You can customize the titles and descriptions of generated API pages using the public_title, private_title, public_description, and private_description parameters.
By default, the system automatically generates appropriate titles based on the page type:
- Single public page (
public=true, private=false): Title is "Public API" - Single private page (
public=false, private=true): Title is "Private API" - Split pages (
public=true, private=true): Two sub-pages with nav labels "Public" and "Private"
Override the default titles with custom text:
CTBase.automatic_reference_documentation(;
# ...
public=false,
private=true,
private_title="Internal Implementation",
filename="internals",
)Result: The private page will display "Internal Implementation" instead of "Private API".
Customize the introductory text that appears below the title:
CTBase.automatic_reference_documentation(;
# ...
public=true,
private=false,
public_title="User API",
public_description="""
This page documents the public interface
for end users. All functions listed here
are stable and safe to use in your
applications.
""",
filename="api",
)Result: The page will show your custom title and description instead of the defaults.
When generating split pages, you can customize both public and private titles:
CTBase.automatic_reference_documentation(;
# ...
public=true,
private=true,
public_title="Exported Functions",
public_description="Stable API for end users.",
private_title="Internal Functions",
private_description="Implementation details for contributors.",
filename="api",
)Result: Two pages are created with your custom titles and descriptions.
If you pass empty strings (the default), the system uses the standard titles and descriptions:
CTBase.automatic_reference_documentation(;
# ...
public_title="", # Uses default: "Public API"
private_title="", # Uses default: "Private API"
public_description="", # Uses default description
private_description="", # Uses default description
# ...
)This allows you to selectively customize only the titles or descriptions you want to change.
When your package uses extensions (weak dependencies), you should check if they're loaded before documenting them in api_reference.jl:
# Check if the extension is loaded
MyExtension = Base.get_extension(MyPackage, :MyExtension)
if !isnothing(MyExtension)
push!(
pages,
CTBase.automatic_reference_documentation(;
subdirectory=".",
primary_modules=[MyExtension => ["ext/MyExtension.jl"]],
external_modules_to_document=[MyPackage],
# ...
title="MyExtension",
filename="my_extension",
),
)
endIn docs/make.jl, use with_api_reference() (a helper defined in your own docs/api_reference.jl) to integrate the generated pages:
using Documenter
using CTBase
include("api_reference.jl")
with_api_reference(dirname(@__DIR__)) do api_pages
makedocs(;
# ...
pages=[
"Introduction" => "index.md",
"API Reference" => api_pages,
# ...
],
)
endwith_api_reference() is a project-level wrapper (not part of CTBase) that:
- Calls
generate_api_reference()to register configurations. - Passes the resulting pages to your
makedocs()call. - Cleans up temporary generated files after the build.
The DocumenterReference extension recognizes several documentation element types:
DOCTYPE_ABSTRACT_TYPE: Abstract type declarationsDOCTYPE_STRUCT: Concrete struct typesDOCTYPE_FUNCTION: Functions and callablesDOCTYPE_MACRO: Macros (names starting with@)DOCTYPE_MODULE: SubmodulesDOCTYPE_CONSTANT: Constants and non-function values
These types are automatically detected and organized in the generated documentation.
# In api_reference.jl
function generate_api_reference(src_dir)
pages = []
# Main module
push!(pages, CTBase.automatic_reference_documentation(;
subdirectory=".",
primary_modules=[MyPackage => [joinpath(src_dir, "MyPackage.jl")]],
title="MyPackage API",
filename="api",
))
# Check and document extensions
for (ext_name, ext_files) in [
(:PlotExt, ["ext/PlotExt.jl"]),
(:OptimExt, ["ext/OptimExt.jl"])
]
ext = Base.get_extension(MyPackage, ext_name)
if !isnothing(ext)
push!(pages, CTBase.automatic_reference_documentation(;
subdirectory=".",
primary_modules=[ext => ext_files],
external_modules_to_document=[MyPackage],
title="$ext_name Extension",
filename=lowercase(string(ext_name)),
))
end
end
return pages
end# Public API for users
push!(pages, CTBase.automatic_reference_documentation(;
subdirectory=".",
primary_modules=[MyPackage],
public=true,
private=false,
title="Public API",
filename="api_public",
))
# Private API for developers
push!(pages, CTBase.automatic_reference_documentation(;
subdirectory=".",
primary_modules=[MyPackage],
public=false,
private=true,
title="Internal API",
filename="api_private",
))CTBase.automatic_reference_documentation(;
subdirectory=".",
primary_modules=[MyPackage],
exclude=Symbol[
:eval, # Compiler-generated
:include, # Compiler-generated
:__init__, # Internal initialization
:PRIVATE_CONST, # Internal constant
],
title="MyPackage API",
filename="api",
)Problem: Extension exists but doesn't appear in documentation
Solution: Ensure the extension is loaded before generating docs:
# In docs/make.jl
using MyPackage
using OptionalDependency # Load the extension
# Now the extension will be available
const MyExt = Base.get_extension(MyPackage, :MyExt)Problem: Functions are listed but have no documentation
Solution: Check that:
- Docstrings are properly formatted with
""" - Source files are correctly specified in
primary_modules - The module is properly loaded
Problem: Documentation includes internal/generated symbols
Solution: Use the exclude parameter with symbols (not strings):
exclude=[:eval, :include] # Exclude compiler-generated symbolsProblem: Extended Base methods don't appear
Solution: Add Base to external_modules_to_document:
external_modules_to_document=[Base, Core]Error: ExtensionError: missing dependencies
Solution: The DocumenterReference extension requires Documenter, Markdown, and MarkdownAST. Ensure they're in your docs environment:
# In docs/Project.toml
[deps]
Documenter = "..."
Markdown = "..."
MarkdownAST = "..."- Exclude internal symbols: Use the
excludeparameter to hide implementation details or compiler-generated symbols - Separate public and private: Create separate pages for public and private APIs to keep the end-user documentation focused
- Document external modules: Use
external_modules_to_documentto include methods from other packages that your package extends (e.g.,BaseorPlots) - Check extensions before documenting: Always use
Base.get_extension()to safely check for optional dependencies before callingautomatic_reference_documentationon them - Use meaningful titles: Choose clear, descriptive titles for each documentation page
- Organize by module: Group related functionality together
- Keep it up-to-date: Regenerate documentation with each release
- Test documentation builds: Include documentation building in your CI pipeline
name: Documentation
on:
push:
branches: [main]
tags: ['*']
pull_request:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: julia-actions/setup-julia@v1
- name: Install dependencies
run: |
julia --project=docs -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate()'
- name: Build documentation
run: julia --project=docs docs/make.jl
- name: Deploy to GitHub Pages
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./docs/buildThe 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.
- Exception Handling: Documenting exception types
- Test Runner Guide: Testing documentation examples
- Coverage Guide: Ensuring documentation coverage