diff --git a/docs/documenter_figure_block.jl b/docs/documenter_figure_block.jl new file mode 100644 index 0000000..e8652a3 --- /dev/null +++ b/docs/documenter_figure_block.jl @@ -0,0 +1,87 @@ +#= + +# Figure blocks + +The figure block is just a rejiggered example block, with the MIME-type set + +=# + +""" + abstract type FigureBlocks + +This type encodes a block denoted by `@figure`. +""" +abstract type FigureBlocks <: Documenter.Expanders.NestedExpanderPipeline end + + +Documenter.Selectors.order(::Type{FigureBlocks}) = 8.0 # like @example +Documenter.Selectors.matcher(::Type{FigureBlocks}, node, page, doc) = Documenter.iscode(node, r"^@figure") + +# TODO: we have to have a better / less fragile way to do this than plain text references! +module MakieDocsHelpers4 + struct AsMIME{M<:MIME,V} + mime::M + value::V + end + + Base.show(io::IO, m::MIME, a::AsMIME{MIME}) where MIME = show(io, m, a.value) +end + +function Documenter.Selectors.runner(::Type{FigureBlocks}, node, page, doc) + el = node.element + infoexpr = Meta.parse(el.info) + args = infoexpr.args[3:end] + if !isempty(args) && args[1] isa Symbol + blockname = string(args[1]) + kwargs = args[2:end] + else + blockname = "" + kwargs = args + end + kwargs = Dict(map(kwargs) do expr + if !(expr isa Expr) && expr.head !== :(=) && length(expr.args) == 2 && expr.args[1] isa Symbol && expr.args[2] isa Union{String,Number,Symbol} + error("Invalid keyword arg expression: $expr") + end + expr.args[1] => expr.args[2] + end) + el.info = "@example $blockname" + el.code = transform_figure_code(el.code; kwargs...) + Documenter.Selectors.runner(Documenter.Expanders.ExampleBlocks, node, page, doc) +end + +function _mime_from_format(fmt::String, backend::Symbol) + return if fmt in ("png", "jpeg") # these are supported by all backends! + "image/$fmt" + elseif fmt == "svg" + @assert backend == :CairoMakie "Only CairoMakie can emit `$fmt` files. Please either change the mime in your `@figure` block to a raster format like PNG, or change the backend to CairoMakie." + "image/svg+xml" + elseif fmt in ("pdf", "eps") + @assert backend == :CairoMakie "Only CairoMakie can emit `$fmt` files. Please either change the mime in your `@figure` block to a raster format like PNG, or change the backend to CairoMakie." + "application/$fmt" + elseif fmt == "html" + @assert backend == :WGLMakie "Only WGLMakie can emit `$fmt` files. Please either change the mime in your `@figure` block to a raster format like PNG, or change the backend to WGLMakie." + "text/html" + else + error("Unknown format `$fmt` detected.") + end +end + +function transform_figure_code(code::String; backend::Symbol = :CairoMakie, type = "png", kwargs...) + backend in (:CairoMakie, :GLMakie, :WGLMakie, :RPRMakie) || error("Invalid backend $backend") + mimetype = _mime_from_format(type, backend) + # All this code is within the Documenter runner module's scope, so we have to go up one level to go to Main. + # I am wondering if, in theory, we should actually just access Main directly? + """ + import $backend # hide + $(backend).activate!() # hide + import Main.MakieDocsHelpers4 # hide + var"#result" = begin # hide + $code + end # hide + if var"#result" isa Makie.FigureLike # hide + MakieDocsHelpers4.AsMIME(MIME"$mimetype"(), var"#result") # hide + else # hide + var"#result" # hide + end # hide + """ +end \ No newline at end of file diff --git a/docs/make.jl b/docs/make.jl index dbecb00..08f9ba5 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -2,7 +2,9 @@ using SwarmMakie using Documenter, DocumenterVitepress, Literate using CairoMakie -CairoMakie.activate!(type="svg", pt_per_unit = 0.75) +CairoMakie.activate!(type="svg", pt_per_unit = 1) + +include("documenter_figure_block.jl") DocMeta.setdocmeta!(SwarmMakie, :DocTestSetup, :(using SwarmMakie); recursive=true) @@ -28,6 +30,10 @@ function _add_meta_edit_link_generator(path) end end +function _replace_example_with_figure(input) + return replace(input, "```@example" => "```@figure") +end + # First letter of `str` is made uppercase and returned ucfirst(str::String) = string(uppercase(str[1]), str[2:end]) @@ -43,7 +49,7 @@ function process_literate_recursive!(pages::Vector{Any}, path::String; source_pa Literate.markdown( path, output_dir; flavor = Literate.CommonMarkFlavor(), - postprocess = _add_meta_edit_link_generator(joinpath(relpath(source_path, output_dir), relative_path)) + postprocess = _add_meta_edit_link_generator(joinpath(relpath(source_path, output_dir), relative_path)) ∘ _replace_example_with_figure ) push!(pages, joinpath("source", splitext(relative_path)[1] * ".md")) end @@ -59,7 +65,7 @@ withenv("JULIA_DEBUG" => "Literate") do # allow Literate debug output to escape end # As a special case, literatify the examples.jl file in docs/src to Documenter markdown -Literate.markdown(joinpath(@__DIR__, "src", "examples.jl"), joinpath(@__DIR__, "src"); flavor = Literate.DocumenterFlavor()) +Literate.markdown(joinpath(@__DIR__, "src", "examples.jl"), joinpath(@__DIR__, "src"); flavor = Literate.DocumenterFlavor(), postprocess = _replace_example_with_figure) makedocs(; modules=[SwarmMakie], diff --git a/docs/src/algorithms.md b/docs/src/algorithms.md index f4dfbb7..5c8ab53 100644 --- a/docs/src/algorithms.md +++ b/docs/src/algorithms.md @@ -10,7 +10,7 @@ In addition, SwarmMakie offers jittered scatter plots as algorithms to `beeswarm Here's a comparison of all the available algorithms: -```@example all_algorithms +```@figure all_algorithms using SwarmMakie, CairoMakie algorithms = [NoBeeswarm() SimpleBeeswarm() WilkinsonBeeswarm(); UniformJitter() PseudorandomJitter() QuasirandomJitter()] fig = Figure(; size = (800, 450)) diff --git a/docs/src/gutters.md b/docs/src/gutters.md index 7087bcf..f147e8e 100644 --- a/docs/src/gutters.md +++ b/docs/src/gutters.md @@ -1,6 +1,6 @@ # Gutters -```@example gutters +```@figure gutters; backend=:CairoMakie; type="svg" using SwarmMakie, CairoMakie xs = rand(1:10, 2000) beeswarm(xs, rand(2000); gutter = 0.3, color = xs) @@ -14,7 +14,7 @@ A nice gutter size to avoid overlap in neighboring categories ranges between `0. ## Examples -```@example gutters +```@figure gutters using SwarmMakie, CairoMakie f, a, p = beeswarm( rand(1:3, 300), randn(300); @@ -23,7 +23,7 @@ f, a, p = beeswarm( p.gutter = 0.5 ``` Note the warning messages printed here! These can be helpful to diagnose when your data is moving too far out of the gutter, but you can turn them off by passing `gutter_threshold = false` or setting the `gutter_threshold` to a higher value (must be an `Int` and >0). -```@example gutters +```@figure gutters f ``` diff --git a/docs/src/introduction.md b/docs/src/introduction.md index 487c355..10dab2d 100644 --- a/docs/src/introduction.md +++ b/docs/src/introduction.md @@ -14,7 +14,7 @@ Being a Makie recipe, you can also use this with AlgebraOfGraphics. Here's a quick example to get you started: -```@example quickstart +```@figure quickstart using CairoMakie, SwarmMakie xs = rand(1:3, 40) ys = randn(40) @@ -27,7 +27,7 @@ f As a Makie recipe, `beeswarm` also composes with AlgebraOfGraphics! -```@example aog +```@figure aog using AlgebraOfGraphics, CairoMakie, SwarmMakie using RDatasets, DataFrames iris = dataset("datasets", "iris")