From 6afe87f75f0fc658daac6fbade89e933f84c2471 Mon Sep 17 00:00:00 2001 From: MilesCranmer Date: Sat, 29 Jun 2024 19:12:15 +0100 Subject: [PATCH 1/9] Make header customizable --- src/Interfaces.jl | 1 + src/documentation.jl | 5 +++++ src/interface.jl | 5 ++--- 3 files changed, 8 insertions(+), 3 deletions(-) create mode 100644 src/documentation.jl diff --git a/src/Interfaces.jl b/src/Interfaces.jl index 26d97ed..fdbaa2d 100644 --- a/src/Interfaces.jl +++ b/src/Interfaces.jl @@ -10,6 +10,7 @@ export @implements, @interface include("arguments.jl") include("interface.jl") +include("documentation.jl") include("implements.jl") include("test.jl") diff --git a/src/documentation.jl b/src/documentation.jl new file mode 100644 index 0000000..be1bc08 --- /dev/null +++ b/src/documentation.jl @@ -0,0 +1,5 @@ +function header(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 diff --git a/src/interface.jl b/src/interface.jl index 6c0cd82..6e279e2 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -81,12 +81,11 @@ 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.header($interface) @doc """ $(" ") $interfacesym - An Interfaces.jl `Interface` with mandatory components `$m_keys` and optional components `$o_keys`. + $header $description """ $interface From 8072ea214797638ada987c9287cd4276e924292b Mon Sep 17 00:00:00 2001 From: MilesCranmer Date: Sat, 29 Jun 2024 19:12:38 +0100 Subject: [PATCH 2/9] Add `# Extended help` section to generated docs --- src/documentation.jl | 21 +++++++++++++++++++++ src/interface.jl | 5 ++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/documentation.jl b/src/documentation.jl index be1bc08..3b7529a 100644 --- a/src/documentation.jl +++ b/src/documentation.jl @@ -3,3 +3,24 @@ function header(interface::Type{<: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})) + m_keys = mandatory_keys(interface) + o_keys = optional_keys(interface) + + io_buf = IOBuffer() + # ^(More efficient and readable than string concatenation) + + println(io_buf, "# Extended help\n") + println(io_buf, "## Mandatory keys:\n") + for (key, (value, _)) in m_keys + println(io_buf, "* `$key`: $value") + end + + println(io_buf, "\n## Optional keys:\n") + for (key, (value, _)) in o_keys + println(io_buf, "* `$key`: $value") + end + + return String(take!(io_buf)) +end diff --git a/src/interface.jl b/src/interface.jl index 6e279e2..e0e8254 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -81,13 +81,16 @@ macro interface(interface::Symbol, type, components, description) # Generate a docstring for the interface let description=$description, interfacesym=$(QuoteNode(interface)), - header=$Interfaces.header($interface) + header=$Interfaces.header($interface), + extended_help=$Interfaces.extended_help($interface) @doc """ $(" ") $interfacesym $header $description + + $extended_help """ $interface end end |> esc From a8f20e70e96f44c9fe96129630c0e86b0888e854 Mon Sep 17 00:00:00 2001 From: MilesCranmer Date: Sat, 29 Jun 2024 19:37:29 +0100 Subject: [PATCH 3/9] account for multiple methods in extended help --- src/documentation.jl | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/src/documentation.jl b/src/documentation.jl index 3b7529a..f19f653 100644 --- a/src/documentation.jl +++ b/src/documentation.jl @@ -4,22 +4,37 @@ function header(interface::Type{<:Interface}) return "An Interfaces.jl `Interface` with mandatory components `$m_keys` and optional components `$o_keys`." end -function extended_help(@nospecialize(interface::Type{<:Interface})) - m_keys = mandatory_keys(interface) - o_keys = optional_keys(interface) +function extended_help(interface::Type{<:Interface}) + comp = components(interface) io_buf = IOBuffer() # ^(More efficient and readable than string concatenation) - println(io_buf, "# Extended help\n") - println(io_buf, "## Mandatory keys:\n") - for (key, (value, _)) in m_keys - println(io_buf, "* `$key`: $value") + println(io_buf, "# Extended help") + !isempty(comp.mandatory) && println(io_buf, "\n## Mandatory keys:\n") + for key in keys(comp.mandatory) + values = comp.mandatory[key] + if values isa Tuple + println(io_buf, "* `$key`:") + for value in values + println(io_buf, " * $(first(value))") + end + else + println(io_buf, "* `$key`: $(first(values))") + end end - println(io_buf, "\n## Optional keys:\n") - for (key, (value, _)) in o_keys - println(io_buf, "* `$key`: $value") + !isempty(comp.optional) && println(io_buf, "\n## Optional keys:\n") + for key in keys(comp.optional) + values = comp.optional[key] + if values isa Tuple + println(io_buf, "* `$key`:") + for value in values + println(io_buf, " * $(first(value))") + end + else + println(io_buf, "* `$key`: $(first(values))") + end end return String(take!(io_buf)) From af4d916feb5a51e19fe1ca4f3f2495d67eb7e6df Mon Sep 17 00:00:00 2001 From: MilesCranmer Date: Sat, 29 Jun 2024 19:37:41 +0100 Subject: [PATCH 4/9] add test for generated docs --- test/advanced.jl | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/test/advanced.jl b/test/advanced.jl index 9d1e06f..51fed14 100644 --- a/test/advanced.jl +++ b/test/advanced.jl @@ -102,3 +102,46 @@ 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 + +# Test generated docs +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) From 6c95d370bf36804799bca1a906855376233ff0d2 Mon Sep 17 00:00:00 2001 From: Miles Cranmer Date: Sun, 30 Jun 2024 12:09:01 +0100 Subject: [PATCH 5/9] Apply suggestions from code review Co-authored-by: Rafael Schouten --- src/documentation.jl | 4 ++-- src/interface.jl | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/documentation.jl b/src/documentation.jl index f19f653..18d52ef 100644 --- a/src/documentation.jl +++ b/src/documentation.jl @@ -1,10 +1,10 @@ -function header(interface::Type{<:Interface}) +function _help_header(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(interface::Type{<:Interface}) +function _extended_help(interface::Type{<:Interface}) comp = components(interface) io_buf = IOBuffer() diff --git a/src/interface.jl b/src/interface.jl index e0e8254..449e5ba 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -81,8 +81,8 @@ macro interface(interface::Symbol, type, components, description) # Generate a docstring for the interface let description=$description, interfacesym=$(QuoteNode(interface)), - header=$Interfaces.header($interface), - extended_help=$Interfaces.extended_help($interface) + header=$Interfaces._help_header($interface), + extended_help=$Interfaces._extended_help($interface) @doc """ $(" ") $interfacesym From 48f08f11c22079f1e512f4578c5eac2779dce439 Mon Sep 17 00:00:00 2001 From: Miles Cranmer Date: Sun, 30 Jun 2024 12:10:04 +0100 Subject: [PATCH 6/9] update extended help function in test --- test/advanced.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/advanced.jl b/test/advanced.jl index 51fed14..fcea32d 100644 --- a/test/advanced.jl +++ b/test/advanced.jl @@ -116,7 +116,7 @@ expected_extended_help = """# Extended help * inversion stable * inversion works""" -@test strip(Interfaces.extended_help(Group.GroupInterface)) == strip(expected_extended_help) +@test strip(Interfaces._extended_help(Group.GroupInterface)) == strip(expected_extended_help) expected_docs = """``` From f8416fc47f44c76e5c49f82c7b05deeabcfed955 Mon Sep 17 00:00:00 2001 From: MilesCranmer Date: Sun, 30 Jun 2024 13:54:53 +0100 Subject: [PATCH 7/9] fix generated extended help for incompatible format --- src/documentation.jl | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/src/documentation.jl b/src/documentation.jl index 18d52ef..c87817b 100644 --- a/src/documentation.jl +++ b/src/documentation.jl @@ -12,30 +12,31 @@ function _extended_help(interface::Type{<:Interface}) println(io_buf, "# Extended help") !isempty(comp.mandatory) && println(io_buf, "\n## Mandatory keys:\n") - for key in keys(comp.mandatory) - values = comp.mandatory[key] - if values isa Tuple - println(io_buf, "* `$key`:") - for value in values - println(io_buf, " * $(first(value))") - end - else - println(io_buf, "* `$key`: $(first(values))") - end - end + _list_keys(io_buf, comp.mandatory) !isempty(comp.optional) && println(io_buf, "\n## Optional keys:\n") - for key in keys(comp.optional) - values = comp.optional[key] - if values isa Tuple - println(io_buf, "* `$key`:") + _list_keys(io_buf, comp.optional) + + return String(take!(io_buf)) +end + +function _list_keys(io::IO, 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_buf, " * $(first(value))") + println(io, " * $(first(value))") end + elseif values isa Pair + # Such as `iterate = "description" => f` + println(io, ": $(first(values))") else - println(io_buf, "* `$key`: $(first(values))") + # all other cases, like `iterate = f` + println(io) end end - - return String(take!(io_buf)) + return nothing end From 6ab91f9af7d010824c2225a206beb5f537f33a7d Mon Sep 17 00:00:00 2001 From: MilesCranmer Date: Sun, 30 Jun 2024 14:24:58 +0100 Subject: [PATCH 8/9] avoid specialization on documentation generation --- src/documentation.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/documentation.jl b/src/documentation.jl index c87817b..cdfd823 100644 --- a/src/documentation.jl +++ b/src/documentation.jl @@ -1,10 +1,10 @@ -function _help_header(interface::Type{<:Interface}) +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(interface::Type{<:Interface}) +function _extended_help(@nospecialize(interface::Type{<:Interface})) comp = components(interface) io_buf = IOBuffer() @@ -20,7 +20,7 @@ function _extended_help(interface::Type{<:Interface}) return String(take!(io_buf)) end -function _list_keys(io::IO, component) +function _list_keys(io::IO, @nospecialize(component)) for key in keys(component) print(io, "* `$key`") values = component[key] From e691b2643d4a20ffc9af689aa9d687629319f003 Mon Sep 17 00:00:00 2001 From: MilesCranmer Date: Sun, 30 Jun 2024 23:31:03 +0100 Subject: [PATCH 9/9] move generated docs test to other file --- test/advanced.jl | 42 +------------------------------------ test/test_generated_docs.jl | 42 +++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 41 deletions(-) create mode 100644 test/test_generated_docs.jl diff --git a/test/advanced.jl b/test/advanced.jl index fcea32d..d0e2190 100644 --- a/test/advanced.jl +++ b/test/advanced.jl @@ -103,45 +103,5 @@ using Test #src @test !Interfaces.test(Group.GroupInterface, Int, int_pairs) #src @test_throws ArgumentError Interfaces.test(Group.GroupInterface, Float64, int_pairs) #src -# Test generated docs -expected_extended_help = """# Extended help +include("test_generated_docs.jl") #src -## 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) diff --git a/test/test_generated_docs.jl b/test/test_generated_docs.jl new file mode 100644 index 0000000..7199c98 --- /dev/null +++ b/test/test_generated_docs.jl @@ -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) +