Skip to content

Commit

Permalink
Make list of plugins a keyword arg
Browse files Browse the repository at this point in the history
Instead of passing plugin objects to `makedocs` as positional arguments
(which was undocumented), they are now passed as a a keyword argument
`plugins`.

The docstrings of the `Plugin` type and the `getplugin` function have
been corrected. They were at best misleading, and at worst flat-out
wrong.
  • Loading branch information
goerz committed Sep 10, 2023
1 parent a67c958 commit 41ddf34
Show file tree
Hide file tree
Showing 6 changed files with 42 additions and 28 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

**For upgrading:** The JS file can still be include by passing it to the `assets` keyword of `format = HTML(...)`. However, it will likely conflict with Documenter's default search implementation. If you require an API to override Documenter's search engine, please open an issue.

* Plugin objects which were formally passed as (undocumented) positional keyword arguments to `makedocs` are now given as elements of a list `plugins` passed as a keyword argument.

### Added

* Doctest filters can now be specified as regex/substitution pairs, i.e. `r"..." => s"..."`, in order to control the replacement (which defaults to the empty string, `""`). ([#1989], [#1271])
Expand Down
14 changes: 8 additions & 6 deletions src/Documenter.jl
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,15 @@ const ERROR_NAMES = [:autodocs_block, :cross_references, :docs_block, :doctest,
abstract type Plugin end
Any plugin that needs to either solicit user input or store information in a
[`Document`](@ref) should create a subtype of `Plugin`. The
subtype, `T <: Documenter.Plugin`, must have an empty constructor `T()` that
initialized `T` with the appropriate default values.
[`Document`](@ref) should create a subtype of `Plugin`, i.e., `T <: DocumenterPlugin`.
To retrieve the values stored in `T`, the plugin can call [`Documenter.getplugin`](@ref).
If `T` was passed to [`makedocs`](@ref), the passed type will be returned. Otherwise,
a new `T` object will be created.
Initialized objects of type `T` can be elements of the list of `plugins` passed as a
keyword argument to [`makedocs`](@ref).
A plugin may retrieve the existing object holding its state via the
[`Documenter.getplugin`](@ref) function. Alternatively, `getplugin` can also instantiate
`T()` on demand, if there is no existing object. This requires that `T` implements an empty
constructor `T()`.
"""
abstract type Plugin end

Expand Down
24 changes: 12 additions & 12 deletions src/documents.jl
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ struct Document
blueprint :: DocumentBlueprint
end

function Document(plugins = nothing;
function Document(;
root :: AbstractString = currentdir(),
source :: AbstractString = "src",
build :: AbstractString = "build",
Expand All @@ -376,6 +376,7 @@ function Document(plugins = nothing;
pages :: Vector = Any[],
pagesonly:: Bool = false,
expandfirst :: Vector = String[],
plugins :: Vector = Any[],
repo :: Union{Remotes.Remote, AbstractString} = "",
remotes :: Union{Dict, Nothing} = Dict(),
sitename :: AbstractString = "",
Expand Down Expand Up @@ -455,14 +456,12 @@ function Document(plugins = nothing;
)

plugin_dict = Dict{DataType, Plugin}()
if plugins !== nothing
for plugin in plugins
plugin isa Plugin ||
throw(ArgumentError("$(typeof(plugin)) is not a subtype of `Plugin`."))
haskey(plugin_dict, typeof(plugin)) &&
throw(ArgumentError("only one copy of $(typeof(plugin)) may be passed."))
plugin_dict[typeof(plugin)] = plugin
end
for plugin in plugins
plugin isa Plugin ||
throw(ArgumentError("$(typeof(plugin)) is not a subtype of `Plugin`."))
haskey(plugin_dict, typeof(plugin)) &&
throw(ArgumentError("only one copy of $(typeof(plugin)) may be passed."))
plugin_dict[typeof(plugin)] = plugin
end

blueprint = DocumentBlueprint(
Expand Down Expand Up @@ -798,9 +797,10 @@ end
"""
getplugin(doc::Document, T)
Retrieves the [`Plugin`](@ref Plugin) type for `T` stored in `doc`. If `T` was passed to
[`makedocs`](@ref makedocs), the passed type will be returned. Otherwise, a new `T` object
will be created using the default constructor `T()`.
Retrieves the object for the [`Plugin`](@ref Plugin) sub-type `T` stored in `doc`. If an
object of type `T` was an element of the `plugins` list passed to [`makedocs`](@ref makedocs),
that object will be returned. Otherwise, a new `T` object will be created using the default
constructor `T()`. Subsequent calls to `getplugin(doc, T)` return the same object.
"""
function getplugin(doc::Document, plugin_type::Type{T}) where T <: Plugin
if !haskey(doc.plugins, plugin_type)
Expand Down
11 changes: 8 additions & 3 deletions src/makedocs.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Implements the makedocs() and functions directly related to it.

"""
makedocs(
makedocs(;
root = "<current-directory>",
source = "src",
build = "build",
Expand All @@ -13,6 +13,7 @@
sitename = "",
expandfirst = [],
draft = false,
others...
)
Combines markdown files and inline docstrings into an interlinked document.
Expand Down Expand Up @@ -217,6 +218,10 @@ determined from the source file path. E.g. for `src/foo.md` it is set to `build/
Note that `workdir` does not affect doctests.
**`plugins`** is a list of [`Documenter.Plugin`](@ref) objects. Use as directed by the
documentation of a third-party plugin. For any subtype `T <: Plugin`, the
`plugins` list may contain at most a single object of type `T`.
## Output formats
**`format`** allows the output format to be specified. The default format is
Expand All @@ -233,8 +238,8 @@ information.
A guide detailing how to document a package using Documenter's [`makedocs`](@ref) is provided
in the [setup guide in the manual](@ref Package-Guide).
"""
function makedocs(components...; debug = false, format = HTML(), kwargs...)
document = Documenter.Document(components; format=format, kwargs...)
function makedocs(; debug = false, format = HTML(), kwargs...)
document = Documenter.Document(; format=format, kwargs...)
# Before starting the build pipeline, we empty out the subtype cache used by
# Selectors.dispatch. This is to make sure that we pick up any new selector stages that
# may have been added to the selector pipelines between makedocs calls.
Expand Down
17 changes: 11 additions & 6 deletions test/plugins/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ function Documenter.Selectors.runner(::Type{_TestPlugins}, doc)
B.processed = true
@test B isa _TestPluginB

# subsequent calls to getplugin() should return the same object
B2 = Documenter.getplugin(doc, _TestPluginB)
@test B2.processed
@test B2 === B

# Missing object (no empty constructor)
@test !(_TestPluginC in keys(doc.plugins))
@test_throws MethodError begin
Expand All @@ -75,8 +80,8 @@ end
A = _TestPluginA(false)
@test !(A.processed)
@test !(_TestPluginB().processed)
@test makedocs(
_RunPluginTests(true), A;
@test makedocs(;
plugins=[_RunPluginTests(true), A],
sitename="-", modules = [PluginsTestModule], warnonly=false
) === nothing
@test A.processed = true
Expand All @@ -90,17 +95,17 @@ A = _TestPluginA(false)
# never true, and we check here the specific error that is being thrown if
# someone were to try it.
@test_throws ArgumentError("DataType is not a subtype of `Plugin`.") begin
makedocs(
_RunPluginTests(false), _TestPluginB;
makedocs(;
plugins=[_RunPluginTests(false), _TestPluginB],
sitename="-", modules = [PluginsTestModule], warnonly=false
)
end


# Only one instance of any given Plugin can be passed.
try # Use try-catch get get around @test_throws limitations in Julia 1.6
makedocs(
_RunPluginTests(false), _TestPluginA(true), _TestPluginA(false);
makedocs(;
plugins=[_RunPluginTests(false), _TestPluginA(true), _TestPluginA(false)],
sitename="-", modules = [PluginsTestModule], warnonly=false
)
@test false # makedocs should have thrown an ArgumentError
Expand Down
2 changes: 1 addition & 1 deletion test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ end
@quietly include("workdir/tests.jl")

# Passing a writer positionally (https://github.com/JuliaDocs/Documenter.jl/issues/1046)
@test_throws ArgumentError makedocs(sitename="", HTML())
@test_throws MethodError makedocs(sitename="", HTML())

# Running doctest() on our own manual
@info "doctest() Documenter's manual"
Expand Down

0 comments on commit 41ddf34

Please sign in to comment.