Skip to content

Commit

Permalink
Generate # Extended help with all component descriptions (#47)
Browse files Browse the repository at this point in the history
* Make header customizable

* Add `# Extended help` section to generated docs

* account for multiple methods in extended help

* add test for generated docs

* Apply suggestions from code review

Co-authored-by: Rafael Schouten <[email protected]>

* update extended help function in test

* fix generated extended help for incompatible format

* avoid specialization on documentation generation

* move generated docs test to other file

---------

Co-authored-by: Rafael Schouten <[email protected]>
  • Loading branch information
MilesCranmer and rafaqz authored Jul 1, 2024
1 parent c421e6d commit 74628a7
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 3 deletions.
1 change: 1 addition & 0 deletions src/Interfaces.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export @implements, @interface

include("arguments.jl")
include("interface.jl")
include("documentation.jl")
include("implements.jl")
include("test.jl")

Expand Down
42 changes: 42 additions & 0 deletions src/documentation.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
function _help_header(@nospecialize(interface::Type{<:Interface}))
m_keys = mandatory_keys(interface)
o_keys = optional_keys(interface)
return "An Interfaces.jl `Interface` with mandatory components `$m_keys` and optional components `$o_keys`."
end

function _extended_help(@nospecialize(interface::Type{<:Interface}))
comp = components(interface)

io_buf = IOBuffer()
# ^(More efficient and readable than string concatenation)

println(io_buf, "# Extended help")
!isempty(comp.mandatory) && println(io_buf, "\n## Mandatory keys:\n")
_list_keys(io_buf, comp.mandatory)

!isempty(comp.optional) && println(io_buf, "\n## Optional keys:\n")
_list_keys(io_buf, comp.optional)

return String(take!(io_buf))
end

function _list_keys(io::IO, @nospecialize(component))
for key in keys(component)
print(io, "* `$key`")
values = component[key]
if values isa Tuple && all(Base.Fix2(isa, Pair), values)
# Such as `iterate = ("description1" => f, "description2" => g)`
println(io, ":")
for value in values
println(io, " * $(first(value))")
end
elseif values isa Pair
# Such as `iterate = "description" => f`
println(io, ": $(first(values))")
else
# all other cases, like `iterate = f`
println(io)
end
end
return nothing
end
8 changes: 5 additions & 3 deletions src/interface.jl
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,16 @@ macro interface(interface::Symbol, type, components, description)
# Generate a docstring for the interface
let description=$description,
interfacesym=$(QuoteNode(interface)),
m_keys=$Interfaces.mandatory_keys($interface),
o_keys=$Interfaces.optional_keys($interface)
header=$Interfaces._help_header($interface),
extended_help=$Interfaces._extended_help($interface)
@doc """
$(" ") $interfacesym
An Interfaces.jl `Interface` with mandatory components `$m_keys` and optional components `$o_keys`.
$header
$description
$extended_help
""" $interface
end
end |> esc
Expand Down
3 changes: 3 additions & 0 deletions test/advanced.jl
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,6 @@ using Test #src
@test Interfaces.test(Group.GroupInterface, Float64) #src
@test !Interfaces.test(Group.GroupInterface, Int, int_pairs) #src
@test_throws ArgumentError Interfaces.test(Group.GroupInterface, Float64, int_pairs) #src

include("test_generated_docs.jl") #src

42 changes: 42 additions & 0 deletions test/test_generated_docs.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
expected_extended_help = """# Extended help
## Mandatory keys:
* `neutral_check`:
* neutral stable
* `multiplication_check`:
* multiplication stable
* `inversion_check`:
* inversion stable
* inversion works"""

@test strip(Interfaces._extended_help(Group.GroupInterface)) == strip(expected_extended_help)


expected_docs = """```
GroupInterface
```
An Interfaces.jl `Interface` with mandatory components `(:neutral_check, :multiplication_check, :inversion_check)` and optional components `()`.
A group is a set of elements with a neutral element where you can perform multiplications and inversions.
The conditions checking the interface accept an `Arguments` object with two fields named `x` and `y`. The type of the first field `x` must be the type you wish to declare as implementing `GroupInterface`.
# Extended help
## Mandatory keys:
* `neutral_check`:
* neutral stable
* `multiplication_check`:
* multiplication stable
* `inversion_check`:
* inversion stable
* inversion works"""

@test strip(string(@doc Group.GroupInterface)) == strip(expected_docs)

0 comments on commit 74628a7

Please sign in to comment.