Documentation Guide
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.
Overview
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
Architecture
Directory Structure
docs/
├── make.jl # Main documentation build script
├── api_reference.jl # API reference generation logic
└── src/
├── index.md # Documentation homepage
├── developers-guide.md # Testing and coverage guide
└── documentation-guide.md # This fileHow It Works
The documentation generation happens in two stages:
api_reference.jl: Definesgenerate_api_reference()which callsCTBase.automatic_reference_documentation()for each modulemake.jl: Callswith_api_reference()which executes the generation and passes the pages toDocumenter.makedocs()
Setting Up API Documentation
Basic Configuration
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",
)Key Parameters
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 pagetitle_in_menu: Title in the navigation menu (defaults totitle)filename: Base filename for the markdown file (without.mdextension)exclude: Vector of symbol names to skip from documentationpublic: Generate public API page (default:true)private: Generate private API page (default:true)external_modules_to_document: Additional modules to search for docstrings (e.g.,[Base])
Public vs. Private API
The public and private flags control which symbols are documented:
Option 1: Public API Only (public=true, private=false)
CTBase.automatic_reference_documentation(;
subdirectory=".",
primary_modules=[MyModule => src("MyModule.jl")],
public=true,
private=false,
title="MyModule API",
filename="api",
)Result: Only exported symbols are documented. This is ideal for end-user documentation.
Option 2: Private API Only (public=false, private=true)
CTBase.automatic_reference_documentation(;
subdirectory=".",
primary_modules=[MyModule => src("MyModule.jl")],
public=false,
private=true,
title="MyModule Internals",
filename="internals",
)Result: Only non-exported (private) symbols are documented. Useful for developer documentation.
Option 3: Both Public and Private (public=true, private=true)
CTBase.automatic_reference_documentation(;
subdirectory=".",
primary_modules=[MyModule => src("MyModule.jl")],
public=true,
private=true,
title="MyModule Complete Reference",
filename="complete_api",
)Result: All symbols are documented in a single page. This provides a comprehensive reference.
Example: CTBase Configuration
Here's how CTBase configures its API documentation in docs/api_reference.jl:
function generate_api_reference(src_dir::String)
# Helper functions to build absolute paths
src(files...) = [abspath(joinpath(src_dir, f)) for f in files]
ext_dir = abspath(joinpath(src_dir, "..", "ext"))
ext(files...) = [abspath(joinpath(ext_dir, f)) for f in files]
# Symbols to exclude from all API pages
EXCLUDE_SYMBOLS = Symbol[:include, :eval]
pages = [
# Main CTBase module - private API only
CTBase.automatic_reference_documentation(;
subdirectory=".",
primary_modules=[CTBase => src("CTBase.jl")],
exclude=EXCLUDE_SYMBOLS,
public=false,
private=true,
title="CTBase",
title_in_menu="CTBase",
filename="ctbase",
),
# Other modules...
]
# Extensions are checked with Base.get_extension
DocumenterReference = Base.get_extension(CTBase, :DocumenterReference)
if !isnothing(DocumenterReference)
push!(
pages,
CTBase.automatic_reference_documentation(;
subdirectory=".",
primary_modules=[DocumenterReference => ext("DocumenterReference.jl")],
external_modules_to_document=[CTBase],
exclude=EXCLUDE_SYMBOLS,
public=false,
private=true,
title="DocumenterReference",
title_in_menu="DocumenterReference",
filename="documenter_reference",
),
)
end
return pages
endHandling Extensions
When your package uses extensions (weak dependencies), you need to check if they're loaded before documenting them:
# 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],
exclude=EXCLUDE_SYMBOLS,
public=false,
private=true,
title="MyExtension",
title_in_menu="MyExtension",
filename="my_extension",
),
)
endThis ensures that:
- Documentation is only generated if the extension is actually loaded
- The extension can reference types and functions from the main package via
external_modules_to_document
Integration with Documenter.jl
In docs/make.jl, use with_api_reference() to integrate the generated pages:
using Documenter
using CTBase
include("api_reference.jl")
with_api_reference(dirname(@__DIR__)) do api_pages
makedocs(;
modules=[CTBase],
authors="Your Name",
repo="https://github.com/yourname/yourpackage.jl",
sitename="YourPackage.jl",
format=Documenter.HTML(;
assets=String[],
),
pages=[
"Introduction" => "index.md",
"Developers Guide" => "developers-guide.md",
"Documentation Guide" => "documentation-guide.md",
"API Reference" => api_pages,
],
checkdocs=:none,
)
endThe with_api_reference() function:
- Generates the API reference pages
- Passes them to your
makedocs()call - Cleans up temporary generated files after the build
DocType System
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.
Best Practices
Exclude internal symbols: Use the
excludeparameter to hide implementation detailsexclude=Symbol[:_internal_helper, :_private_constant]Separate public and private: Create separate pages for public and private APIs
# Public API CTBase.automatic_reference_documentation(; ..., public=true, private=false, filename="api_public", ) # Private API CTBase.automatic_reference_documentation(; ..., public=false, private=true, filename="api_private", )Document external modules: Use
external_modules_to_documentto include methods from other packagesCTBase.automatic_reference_documentation(; ..., external_modules_to_document=[Base, Documenter], )Check extensions before documenting: Always use
Base.get_extension()to safely check for optional dependenciesMyExt = Base.get_extension(MyPackage, :MyExtension) if !isnothing(MyExt) # Document the extension end
Troubleshooting
Missing Docstrings
If symbols appear without docstrings in the generated documentation, ensure:
- The docstring is defined immediately before the symbol
- The docstring uses the correct Julia docstring syntax (triple quotes)
- The symbol is actually exported or included in your module
Symbols Not Appearing
If expected symbols don't appear in the documentation:
- Check if they're in the
excludelist - Verify the source file path is correct
- Ensure the symbol is defined in the specified source file (not imported)
Extension Not Documented
If an extension's documentation isn't generated:
- Verify the extension is loaded with
Base.get_extension() - Check that the extension file path is correct
- Ensure the extension module is properly defined
Summary
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.