From 9edd0473fa8d0bdc9160569c7b2842b756bbb3df Mon Sep 17 00:00:00 2001 From: Anshul Singhvi Date: Wed, 20 Mar 2024 19:04:45 -0700 Subject: [PATCH 1/5] Add the Documenter figure block implementation by Julius Co-authored-by: Julius Krumbiegel <22495855+jkrumbiegel@users.noreply.github.com> --- docs/documenter_figure_block.jl | 87 +++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 docs/documenter_figure_block.jl diff --git a/docs/documenter_figure_block.jl b/docs/documenter_figure_block.jl new file mode 100644 index 0000000..021acbe --- /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=backend==:CairoMakie ? "svg" : "png", kwargs...) + backend in (:CairoMakie, :GLMakie, :WGLMakie, :RPRMakie) || error("Invalid backend $backend") + mimetype = _mime_from_format(fmt, 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 ..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 From 11fecfbca86e4bba778e2edeee1bc27c1788fbd5 Mon Sep 17 00:00:00 2001 From: Anshul Singhvi Date: Wed, 20 Mar 2024 19:11:31 -0700 Subject: [PATCH 2/5] Implement the figure block syntax --- docs/documenter_figure_block.jl | 4 ++-- docs/make.jl | 10 ++++++++-- docs/src/algorithms.md | 2 +- docs/src/gutters.md | 6 +++--- docs/src/introduction.md | 4 ++-- 5 files changed, 16 insertions(+), 10 deletions(-) diff --git a/docs/documenter_figure_block.jl b/docs/documenter_figure_block.jl index 021acbe..46e4f01 100644 --- a/docs/documenter_figure_block.jl +++ b/docs/documenter_figure_block.jl @@ -68,7 +68,7 @@ end function transform_figure_code(code::String; backend::Symbol = :CairoMakie, type=backend==:CairoMakie ? "svg" : "png", kwargs...) backend in (:CairoMakie, :GLMakie, :WGLMakie, :RPRMakie) || error("Invalid backend $backend") - mimetype = _mime_from_format(fmt, 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? """ @@ -81,7 +81,7 @@ function transform_figure_code(code::String; backend::Symbol = :CairoMakie, type if var"#result" isa Makie.FigureLike # hide MakieDocsHelpers4.AsMIME(MIME"$mimetype"(), var"#result") # hide else # hide - var"result" # 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..fa32e69 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -4,6 +4,8 @@ using CairoMakie CairoMakie.activate!(type="svg", pt_per_unit = 0.75) +include("documenter_figure_block.jl") + DocMeta.setdocmeta!(SwarmMakie, :DocTestSetup, :(using SwarmMakie); recursive=true) ENV["DATADEPS_ALWAYS_ACCEPT"] = 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") From f1a2db8e040ed87ae519de287f134def59318093 Mon Sep 17 00:00:00 2001 From: Anshul Singhvi Date: Thu, 21 Mar 2024 09:37:04 -0400 Subject: [PATCH 3/5] Import explicitly from Main --- docs/documenter_figure_block.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/documenter_figure_block.jl b/docs/documenter_figure_block.jl index 46e4f01..bdb82b7 100644 --- a/docs/documenter_figure_block.jl +++ b/docs/documenter_figure_block.jl @@ -74,7 +74,7 @@ function transform_figure_code(code::String; backend::Symbol = :CairoMakie, type """ import $backend # hide $(backend).activate!() # hide - import ..MakieDocsHelpers4 # hide + import Main.MakieDocsHelpers4 # hide var"#result" = begin # hide $code end # hide From a68d24e24a6a4e55a78f2af9362e791d6461515a Mon Sep 17 00:00:00 2001 From: Anshul Singhvi Date: Thu, 21 Mar 2024 06:42:48 -0700 Subject: [PATCH 4/5] Increase SVG real size --- docs/make.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/make.jl b/docs/make.jl index fa32e69..08f9ba5 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -2,7 +2,7 @@ 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") From 9c3fd726ccdd6d39815713041a28f6fc8c5b73fc Mon Sep 17 00:00:00 2001 From: Anshul Singhvi Date: Sun, 7 Apr 2024 10:12:44 -0400 Subject: [PATCH 5/5] Always default to PNG unless otherwise specified. --- docs/documenter_figure_block.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/documenter_figure_block.jl b/docs/documenter_figure_block.jl index bdb82b7..e8652a3 100644 --- a/docs/documenter_figure_block.jl +++ b/docs/documenter_figure_block.jl @@ -66,7 +66,7 @@ function _mime_from_format(fmt::String, backend::Symbol) end end -function transform_figure_code(code::String; backend::Symbol = :CairoMakie, type=backend==:CairoMakie ? "svg" : "png", kwargs...) +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.