diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index c83e6d72..d7e55439 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -52,7 +52,7 @@ jobs: - name: Build docs run: | cd docs - julia --project -e 'using BooksDocs; BooksDocs.build()' + julia --project -e 'using BooksDocs; M = BooksDocs; BooksDocs.build()' env: GKS_ENCODING: "utf8" GKSwstype: "100" diff --git a/docs/Project.toml b/docs/Project.toml index 4858aaa3..4dcfcd09 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -15,9 +15,11 @@ Latexify = "23fbe1c1-3f47-55db-b15f-69d7ec21a316" MCMCChains = "c7f686f2-ff18-58e9-bc7b-31028e88f75d" Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" +Reexport = "189a3867-3050-52da-a836-e630ba90ab69" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" TOML = "fa267f1f-6049-4f14-aa54-33bafae1ed76" [compat] Latexify = "0.15" MCMCChains = "4.9" +Reexport = "1.1" diff --git a/docs/contents/about.md b/docs/contents/about.md index 872fce90..e3b04114 100644 --- a/docs/contents/about.md +++ b/docs/contents/about.md @@ -1,7 +1,6 @@ # About {#sec:about} -Basically, this package is a wrapper around [Pandoc](https://pandoc.org/){target="_blank"}; similar to [Bookdown](https://bookdown.org){target="_blank"}. -Note that Pandoc does the heavy lifting and this package adds features on top. +Similar to [Bookdown](https://bookdown.org){target="_blank"} this package is, basically, a wrapper around [Pandoc](https://pandoc.org/){target="_blank"}. For websites, this package allows for: - Building a website spanning multiple pages. @@ -17,33 +16,79 @@ One of the main differences with Franklin.jl, Weave.jl and knitr (Bookdown) is t The benefit of this is that you can spawn two separate processes, namely the one to serve your webpages: ```jl -serve_example() +M.serve_example() ``` -and the one where you do the computations for your package `Foo`: +and the one where you do the computations for your package: -```jl -generate_example() +``` +$ julia --project -ie 'using Books' + +julia> gen() +[...] +Updating html ``` This way, the website remains responsive when the computations are running. Thanks to LiveServer.jl and Pandoc, updating the page after changing text or code takes less than a second. -Also, because the `serve` process does relatively few things, it doesn't often crash. -A drawback of this decoupling is that you need to link your text to the correct computation in the Markdown file, whereas in other packages you would insert the code as a string. +Also, because the `serve` process does relatively few things, it almost never crashes. + +As another benefit, the decoupling allows you to have more flexiblity in when you want to run what code. +In combination with Revise.jl, you can quickly update your code and see the updated output. + +Finally, a big difference with this package and other packages is that you decide yourself what you want to show for a code block. +For example, in R + +
+```{r, results='hide'} +print("Hello, world!") +``` ++ +shows the code and not the output. +Instead, in Books, you would write + +
+```jl +sc(raw""" +print("Hello, world!") +""" +) +``` +-The decoupling also allows the output, which you want to include, to be evaluated inside your package, see @sec:embedding-output. -This means that you don't have to define all your dependencies in a `@setup` (Documenter.jl) or `# hideall` (Franklin.jl / Literate.jl) code block. -(Granted, you could work your way around it by only calling methods inside a package.) -The dependencies, such as `using DataFrames`, are available from your package. -This provides all the benefits which Julia packages normally have, such as unit testing and live reloading via Revise.jl. +which is displayed as -As another benefit, all the code which you show in a book can be used via the function name. -So, this avoids naming code blocks like "J3" and "J4", and allows users to load the code from your package and call the functions themselves. -This has multiple benefits, namely +```jl +sc(raw""" +print("Hello, world!") +""" +) +``` + +Here, `sc` is one of the convenience methods exported by Books.jl. +Although this approach is more verbose in some cases, it is also much more flexible. +In essence, you can come up with your own pre- or post-processing logic. +For example, lets write + +
+```jl +code = """ + df = DataFrame(a=[1, 2], b=[3, 4]) + Options(df, caption="A table", label=nothing) + """ +repeat(sco(code), 4) +``` ++ +which shows the code and output (`sco`) 4 times: + +```jl +code = """ + df = DataFrame(a=[1, 2], b=[3, 4]) + Options(df, caption="A table", label=nothing) + """ +repeat(sco(code), 4) +``` -1. it allows for explicitly using the output from one code block as input to another code block, -1. it allows for demonstrating to the reader how to organize code (because, in general the tip is: use functions) _and_ -1. could save the reader from copy and pasting code. -The latter point is due to the fact that the reader can load the package for the book and run the function. -An example for points 1 and 2 is shown in @sec:function_code_blocks. diff --git a/docs/contents/demo.md b/docs/contents/demo.md index bc91c87f..87bd1c1c 100644 --- a/docs/contents/demo.md +++ b/docs/contents/demo.md @@ -20,7 +20,7 @@ For example, to show the Julia version, define a code block like
```jl -julia_version() +M.julia_version() ```@@ -28,24 +28,24 @@ in a Markdown file. Then, in your package, define the method `julia_version()`: ``` -julia_version() = "This book is built with Julia $VERSION." +M.julia_version() = "This book is built with Julia $VERSION." ``` Next, ensure that you call `using Books; gen(; M)`, where `M = YourModule`. Alternatively, if you work on a large project and want to only generate the output for one or more Markdown files in `contents/`, such as `index.md`, use ```jl -markdown_gen_example() +M.markdown_gen_example() ``` Calling `gen` will place the text ```jl -julia_version_example() +M.julia_version_example() ``` at the right path so that it can be included by Pandoc. -You can also embed the output inline with single backticks like +You can also embed output inline with single backticks like ``` `jl julia_version()` @@ -63,55 +63,46 @@ While doing this, it is expected that you also have the browser open and a serve That way, the page is immediately updated when you run `gen`. Note that it doesn't matter where you define the function `julia_version`, as long as it is in your module. -To save yourself some typing, and to allow yourself to get some coffee while Julia gets up to speed, you can start Julia for some package `Foo` with +To save yourself some typing, and to allow yourself to get some coffee while Julia gets up to speed, you can start Julia for your package with ``` -$ julia --project -ie 'using Books; using Foo; M = Foo; gen(; M)' +$ julia --project -ie 'using Books; using MyPackage; M = MyPackage' ``` which allows you to re-generate all the content by calling ``` -julia> gen(; M) -``` - -Also, it allows you to quickly restart Julia after you have updated some constants such as structs. -To re-generate only the content for one method like, for example, the method `my_plot`, use - -``` -julia> gen(M.my_plot) -[...] +julia> gen() ``` To run this method automatically when you make a change in your package, ensure that you loaded [Revise.jl](https://github.com/timholy/Revise.jl) before loading your package and run ``` -julia> f() = gen(M.my_plot); - -julia> entr(f, ["contents"], [M]) +julia> entr(gen, ["contents"], [M]) [...] ``` -Which will automatically run `f()` whenever one of the files in `contents/` changes or any code in the module `M`. +where M is the name of your module. +Which will automatically run `gen()` whenever one of the files in `contents/` changes or any code in the module `M`. In the background, `gen` passes the methods through `convert_output(expr::String, path, out::T)` where `T` can, for example, be a DataFrame or a plot. To show that a DataFrame is converted to a Markdown table, we define a method ```jl -@sc(my_table) +@sc(M.my_table) ``` and add its output to the Markdown file with
```jl -my_table() +M.my_table() ```Then, it will show as ```jl -my_table() +M.my_table() ``` where the caption and the label are inferred from the `path`. @@ -125,14 +116,14 @@ Refer to @tbl:my_table with To show multiple objects, pass a `Vector`: ```jl -@sco(multiple_df_vector) +@sco(M.multiple_df_vector) ``` When you want to control where the various objects are saved, use `Options`. This way, you can pass a informative path with plots for which informative captions, cross-reference labels and image names can be determined. ```jl -@sco(multiple_df_example) +@sco(M.multiple_df_example) ``` To define the labels and/or captions manually, see @sec:labels-captions. @@ -189,14 +180,14 @@ I guess that there is no perfect way here. The benefit of evaluating the user input directly, as Books.jl is doing, seems to be that it is more extensible if I'm not mistaken. Possibly, the reasoning is that R Markdown needs to convert the output directly, whereas Julia's better type system allows for converting in much later stages, but I'm not sure. -> **Tip**: After you run `gen(; M)` with the `Point` struct defined above, the struct will be available in your REPL. +> **Tip**: After you run `gen()` with the `Point` struct defined above, the struct will be available in your REPL. ## Labels and captions {#sec:labels-captions} To set labels and captions, wrap your object in `Options`: ```jl -@sco(options_example) +@sco(M.options_example) ``` which can be referred to with @@ -210,10 +201,10 @@ It is also possible to pass only a caption or a label. This package will attempt to infer missing information from the `path`, `caption` or `label` when possible: ```jl -options_example_doctests() +M.options_example_doctests() ``` -## Function code blocks {#sec:function_code_blocks} +## Obtaining function definitions {#sec:function_code_blocks} So, instead of passing a string which `Books.jl` will evaluate, `Books.jl` can also obtain the code for a method directly. (Thanks to `CodeTracking.@code_string`.) @@ -221,7 +212,7 @@ For example, we can define the following method:
```jl -my_data() +M.my_data() ```@@ -230,41 +221,41 @@ This macro is exported by Books, so ensure that you have `using Books` in your p
```jl -@sco(my_data) +@sco(M.my_data) ```This gives ```jl -@sco(my_data) +@sco(M.my_data) ``` -To only show the source code, use the `-sc` suffix: +To only show the source code, use `@sc`:
```jl -@sc(my_data) +@sc(M.my_data) ```resulting in ```jl -@sc(my_data) +@sc(M.my_data) ``` Since we're using methods as code blocks, we can use the code shown in one code block in another. For example, to determine the mean of column A: ```jl -@sco(my_data_mean) +@sco(M.my_data_mean) ``` -Or, we can show the output inline, namely `jl my_data_mean()`, by using +Or, we can show the output inline, namely `jl M.my_data_mean()`, by using ``` -`jl my_data_mean()` +`jl M.my_data_mean()` ``` ## Plots {#sec:plots} @@ -275,7 +266,7 @@ This is actually a bit tricky, because we want to show vector graphics (SVG) on Therefore, portable network graphics (PNG) images are also created and passed to LaTeX when building a PDF. ```jl -@sco(example_plot) +@sco(M.example_plot) ``` If the output is a string instead of the output you expected, then check whether you load the related packages in time. @@ -284,49 +275,49 @@ For example, for this plot, you need to load AlgebraOfGraphics.jl together with For multiple images, use `Options.(objects, paths)`: ```jl -@sc(multiple_example_plots) +@sc(M.multiple_example_plots) ``` Resulting in @fig:example_plot_2 and @fig:example_plot_3: ```jl -multiple_example_plots() +M.multiple_example_plots() ``` For changing the size, use `axis` from AlgebraOfGraphics: ```jl -@sco(image_options_plot) +@sco(M.image_options_plot) ``` And, for adjusting the caption, use `Options`: ```jl -@sco(combined_options_plot) +@sco(M.combined_options_plot) ``` or the caption can be specified in the Markdown file:
```jl -Options(image_options_plot(); caption="Label specified in Markdown.") +Options(M.image_options_plot(); caption="Label specified in Markdown.") ``````jl -Options(image_options_plot(); caption="Label specified in Markdown.") +Options(M.image_options_plot(); caption="Label specified in Markdown.") ``` ### Plots {#sec:plotsjl} ```jl -@sco(plotsjl) +@sco(M.plotsjl) ``` ### Makie {#sec:makie} ```jl -@sco(makiejl) +@sco(M.makiejl) ``` ## Other notes @@ -342,7 +333,7 @@ So, if the package that you're using has defined a new `show` method, this will For example, for `MCMCChains`, ```jl -@sco(chain) +@sco(M.chain) ``` ### Note box diff --git a/docs/contents/getting-started.md b/docs/contents/getting-started.md index 983423f6..417b4bd6 100644 --- a/docs/contents/getting-started.md +++ b/docs/contents/getting-started.md @@ -7,19 +7,17 @@ The easiest way to get started is to 1. serve your book via: ```jl -serve_example() +M.serve_example() ``` To generate all the Julia output (see @sec:embedding-output for more information) use -```jl -generate_example() ``` +$ julia --project -e 'using Books; using MyPackage; M = MyPackage' -As the number of outputs increases, you might want to only update one output: - -```jl -gen_function_docs() +julia> gen() +[...] +Updating html ``` To avoid code duplication between projects, this package tries to have good defaults for many settings. @@ -39,13 +37,13 @@ You can override settings by placing a `metadata.yml` file at the root directory For example, the metadata for this project contains: ```jl -docs_metadata() +M.docs_metadata() ``` The following defaults are set by Books.jl. ```jl -default_metadata() +M.default_metadata() ``` ## config.toml {#sec:config} @@ -67,13 +65,13 @@ The `pdf_filename` is used by `pdf()` and the `port` setting is used by `serve() For this documentation, the following config is used ```jl -docs_config() +M.docs_config() ``` Which overrides some settings from the following default settings ```jl -default_config() +M.default_config() ``` Here, the `extra_directories` allows you to specify directories which need to be moved into `_build`, which makes them available for the local server and online. diff --git a/docs/contents/index.md b/docs/contents/index.md index 6381635f..bcd1c16f 100644 --- a/docs/contents/index.md +++ b/docs/contents/index.md @@ -5,7 +5,7 @@ This file is only included on the website. ``` ```jl -homepage_intro() +M.homepage_intro() ``` See @sec:about for more information about this package. diff --git a/docs/src/BooksDocs.jl b/docs/src/BooksDocs.jl index 14a71cf3..3980e251 100644 --- a/docs/src/BooksDocs.jl +++ b/docs/src/BooksDocs.jl @@ -6,21 +6,21 @@ import MCMCChains import Statistics import TOML -using AlgebraOfGraphics -using Books -using CairoMakie -using CodeTracking -using DataFrames -using Dates -using Plots +using Reexport +@reexport using AlgebraOfGraphics +@reexport using Books +@reexport using CairoMakie +@reexport using CodeTracking +@reexport using DataFrames +@reexport using Dates +@reexport using Plots -# Defaulting plot to Plots; Makie can use Makie.plot. plot = Plots.plot include("includes.jl") function build() - Books.gen(; M=BooksDocs, fail_on_error=true) + Books.gen(; fail_on_error=true) extra_head = "" Books.build_all(; extra_head) end diff --git a/docs/src/includes.jl b/docs/src/includes.jl index 172315e0..72a89bc7 100644 --- a/docs/src/includes.jl +++ b/docs/src/includes.jl @@ -23,17 +23,6 @@ serve_example() = code_block(raw""" (use CTRL+C to shut down) """) -generate_example() = code_block(raw""" - $ julia --project -e 'using Books; using Foo; M = Foo' - - julia> Books.gen(; M) - Running example() for _gen/example.md - Running julia_version() for _gen/julia_version.md - Running example_plot() for _gen/example_plot.md - Writing plot images for example_plot - [...] - """) - gen_function_docs() = Books.doctest(@doc gen(::Function)) function docs_metadata() @@ -113,16 +102,17 @@ function markdown_gen_example() c = IOCapture.capture() do M = BooksDocs # Update html set to false to avoid Pandoc errors. - gen(["index"]; M, call_html=false) + gen("index", call_html=false) end """ ``` - gen(["index"]; M) + gen("index") ``` ``` - $(c.output) + $(rstrip(c.output)) + Updating html ``` """ end diff --git a/src/generate.jl b/src/generate.jl index 881b5a39..1b1e7217 100644 --- a/src/generate.jl +++ b/src/generate.jl @@ -89,13 +89,13 @@ This is used for things like how to call an image file and a caption. # Examples ```jldoctest -julia> Books.method_name("@some_macro(foo)") +julia> Books.method_name("@some_macro(M.foo)") "foo" -julia> Books.method_name("foo()") +julia> Books.method_name("M.foo()") "foo" -julia> Books.method_name("foo(3)") +julia> Books.method_name("M.foo(3)") "foo_3" julia> Books.method_name("Options(foo(); caption='b')") @@ -105,6 +105,11 @@ julia> Books.method_name("Options(foo(); caption='b')") function method_name(expr::String) remove_macros(expr) = replace(expr, r"@[\w\_]*" => "") expr = remove_macros(expr) + if startswith(expr, '(') + expr = strip(expr, ['(', ')']) + end + remove_modules(expr) = replace(expr, r"^[A-Z][a-zA-Z]*\." => "") + expr = remove_modules(expr) expr = replace(expr, '(' => '_') expr = replace(expr, ')' => "") expr = replace(expr, ';' => "_") @@ -201,11 +206,20 @@ The methods are assumed to be in the module `M` of the caller. Otherwise, specify another module `M`. After calling the methods, this method will also call `html()` to update the site when `call_html == true`. + +!!! note + + If there is anthing that you want to have available when running the code blocks, + just load them inside your REPL (module `Main`) and call `gen()`. + For example, you can define `M = YourModule` to shorten calls to methods in your module. """ -function gen(paths::Vector; M=Main, fail_on_error=false, project="default", call_html=true) + +function gen(paths::Vector{String}; + M=Main, fail_on_error=false, project="default", call_html=true) + mkpath(GENERATED_DIR) paths = [contains(dirname(p), "contents") ? p : expand_path(p) for p in paths] - included_expr = vcat([extract_expr(read(path, String)) for path in paths]...) + included_expr = vcat([extract_expr(read(p, String)) for p in paths]...) f(expr) = evaluate_include(expr, M, fail_on_error) foreach(f, included_expr) if call_html @@ -213,7 +227,16 @@ function gen(paths::Vector; M=Main, fail_on_error=false, project="default", call html(; project) end end -gen(path::String; kwargs...) = gen([path]; kwargs...) + +""" + gen(path::AbstractString; kwargs...) + +Convenience method for passing `path::AbstractString` instead of `paths::Vector`. +""" +function gen(path::AbstractString; kwargs...) + path = string(path)::String + gen([path]; kwargs...) +end function gen(; M=Main, fail_on_error=false, project="default", call_html=true) paths = inputs(project) @@ -223,33 +246,3 @@ function gen(; M=Main, fail_on_error=false, project="default", call_html=true) end gen(paths; M, fail_on_error, project, call_html) end - -""" - gen(f::Function; fail_on_error=false, project="default", call_html=true) - -Populate the file in `Books.GENERATED_DIR` by calling `func`. -This method is useful during development to quickly see the effect of updating your code. -Use with Revise.jl and optionally `Revise.entr`. -After calling `f`, this method will also call `html()` to update the site when `call_html=true`. - -# Example -```jldoctest -julia> module Foo - version() = "This book is built with Julia \$VERSION" - end; - -julia> call_html = false; # To avoid Pandoc errors breaking this jldoctest. - -julia> gen(Foo.version; call_html) -Writing output of `version()` to _gen/version_.md -``` -""" -function gen(f::Function; project="default", call_html=true) - path = joinpath(GENERATED_DIR, "$f.md") - mkpath(GENERATED_DIR) - evaluate_and_write(f) - if call_html - println("Updating html") - html(; project) - end -end diff --git a/src/output.jl b/src/output.jl index 41b775cc..91bdd4c7 100644 --- a/src/output.jl +++ b/src/output.jl @@ -42,20 +42,20 @@ end """ Options(object; - filename::Union{AbstractString,Nothing}=nothing, - caption::Union{AbstractString,Nothing}=nothing, - label::Union{AbstractString,Nothing}=nothing) + filename::Union{AbstractString,Nothing,Missing}=missing, + caption::Union{AbstractString,Nothing,Missing}=missing, + label::Union{AbstractString,Nothing,Missing}=missing) Struct containing an `object` and some meta-information to be passed to the resulting document. The `caption` and `label` options are used by `pandoc-crossref`. """ struct Options object::Any - filename::Union{AbstractString,Nothing} - caption::Union{AbstractString,Nothing} - label::Union{AbstractString,Nothing} + filename::Union{AbstractString,Nothing,Missing} + caption::Union{AbstractString,Nothing,Missing} + label::Union{AbstractString,Nothing,Missing} - Options(object; filename=nothing, caption=nothing, label=nothing) = + Options(object; filename=missing, caption=missing, label=missing) = new(object, filename, caption, label) end @@ -71,8 +71,8 @@ julia> filenames = ["a", "b"]; julia> Options.(objects, filenames) 2-element Vector{Options}: - Options(1, "a", nothing, nothing) - Options(2, "b", nothing, nothing) + Options(1, "a", missing, missing) + Options(2, "b", missing, missing) ``` """ Options(object, filename::AbstractString) = Options(object; filename) @@ -112,6 +112,24 @@ function convert_output(expr, path, out::Code)::String """ end +function plotting_filename(expr, path, package::String) + if path isa AbstractString + file = basename(path) + file, _ = splitext(file) + file = string(file)::String + elseif expr isa AbstractString + file = method_name(expr) + else + # Not determining some random name here, because it would require cleanups too. + msg = """ + It is not possible to write an image without specifying a path. + Use `Options(p; filename=filename)` where `p` is a $package plot. + """ + throw(ErrorException(msg)) + end + string(file)::String +end + """ convert_output(expr, path, options::Options) @@ -123,25 +141,28 @@ see the `pandoc-crossref` documentation for more information on the syntax. ```jldoctest julia> df = DataFrame(A = [1]); -julia> caption = "My DataFrame"; +julia> caption = "My DataFrame."; julia> options = Options(df; caption); -julia> print(Books.convert_output(nothing, nothing, options)) +julia> print(Books.convert_output(missing, missing, options)) | A | | ---:| | 1 | -: My DataFrame +: My DataFrame. ``` """ function convert_output(expr, path, opts::Options)::String object = opts.object filename = opts.filename - if !isnothing(filename) + if !isnothing(filename) || !ismissing(filename) expr = filename + else + # The path is where the md should be written to; not things like images. + name, _ = splitext(path) + expr = string(basename(name))::String end - path = nothing caption = opts.caption label = opts.label convert_output(expr, path, object; caption, label) @@ -195,12 +216,13 @@ Return prettier caption. ```jldoctest julia> Books.prettify_caption("example_table") -"Example table" +"Example table." ``` """ function prettify_caption(caption) caption = replace(caption, '_' => ' ') caption = uppercasefirst(caption) + caption = caption * '.' end """ @@ -231,52 +253,66 @@ end caption_label(expr, caption, label) Return `caption` and `label` for the inputs. -This method sets some reasonable defaults if any of the inputs is missing. +This method sets some reasonable defaults if any of the inputs is nothing or missing. +In this context, `nothing` forces a parameter to be empty, whereas `missing` allows the +parameter to be inferred. +The elements of the output named tuple are never of type `Missing`. # Examples + ```jldoctest -julia> Books.caption_label("foo_bar()", nothing, nothing) -(caption = "Foo bar", label = "foo_bar") +julia> Books.caption_label("foo_bar()", missing, missing) +(caption = "Foo bar.", label = "foo_bar") + +julia> Books.caption_label("foo_bar()", "My caption.", missing) +(caption = "My caption.", label = "foo_bar") -julia> Books.caption_label("foo_bar()", "My caption", nothing) -(caption = "My caption", label = "foo_bar") +julia> Books.caption_label("foo_bar()", "My caption.", nothing) +(caption = "My caption.", label = nothing) -julia> Books.caption_label(nothing, "cap", nothing) -(caption = "cap", label = nothing) +julia> Books.caption_label(missing, "My caption.", missing) +(caption = "My caption.", label = nothing) -julia> Books.caption_label(nothing, nothing, "my_label") -(caption = "My label", label = "my_label") +julia> Books.caption_label(missing, missing, "my_label") +(caption = "My label.", label = "my_label") -julia> Books.caption_label(nothing, nothing, nothing) +julia> Books.caption_label(missing, missing, missing) (caption = nothing, label = nothing) ``` """ function caption_label(expr, caption, label) - if isnothing(expr) && isnothing(caption) && isnothing(label) + if ismissing(expr) && ismissing(caption) && ismissing(label) return (caption=nothing, label=nothing) end - if !isnothing(expr) + original_caption = caption + original_label = label + + if !ismissing(expr) && !isnothing(expr) name = method_name(expr) - if isnothing(label) + if ismissing(label) label = name end - if isnothing(caption) + if ismissing(caption) caption = prettify_caption(name) end - return (caption=caption, label=label) end - if !isnothing(label) - if isnothing(caption) + if !ismissing(label) && !isnothing(label) + if ismissing(caption) caption = prettify_caption(label) end - return (caption=caption, label=label) end - if !isnothing(caption) - return (caption=caption, label=label) + if isnothing(original_caption) || ismissing(caption) + caption = nothing end + + if isnothing(original_label) || ismissing(label) + label = nothing + end + + return (caption=caption, label=label) end """ diff --git a/src/outputs/aog.jl b/src/outputs/aog.jl index b3d7d37a..2e2fe0e4 100644 --- a/src/outputs/aog.jl +++ b/src/outputs/aog.jl @@ -3,19 +3,11 @@ using AlgebraOfGraphics using CairoMakie -function convert_output(expr, path, fg::AlgebraOfGraphics.FigureGrid; caption=nothing, label=nothing) +function convert_output(expr, path, fg::AlgebraOfGraphics.FigureGrid; caption=missing, label=missing) im_dir = joinpath(BUILD_DIR, "im") mkpath(im_dir) - if isnothing(expr) - # Not determining some random name here, because it would require cleanups too. - msg = """ - It is not possible to write an image without specifying a path. - Use `Options(p; filename=filename)` where `p` is a AlgebraOfGraphics plot. - """ - throw(ErrorException(msg)) - end - file = method_name(expr) + file = plotting_filename(expr, path, "AlgebraOfGraphics.jl") println("Writing plot images for $file") svg_filename = "$file.svg" diff --git a/src/outputs/dataframes.jl b/src/outputs/dataframes.jl index f14c80cf..34a98942 100644 --- a/src/outputs/dataframes.jl +++ b/src/outputs/dataframes.jl @@ -15,10 +15,10 @@ julia> print(Books.convert_output("my_table()", nothing, df)) | ---:| | 1 | -: My table {#tbl:my_table} +: My table. {#tbl:my_table} ``` """ -function convert_output(expr, path, out::DataFrame; caption=nothing, label=nothing)::String +function convert_output(expr, path, out::DataFrame; caption=missing, label=missing)::String table = Latexify.latexify(out; env=:mdtable, latex=false) caption, label = caption_label(expr, caption, label) @@ -26,17 +26,11 @@ function convert_output(expr, path, out::DataFrame; caption=nothing, label=nothi return string(table) end - if !isnothing(label) - return """ - $table - : $caption {#tbl:$label} - """ - end + label = isnothing(label) ? "" : "{#tbl:$label}" + caption = isnothing(caption) ? "" : caption - if !isnothing(caption) - return """ + return """ $table - : $caption + : $caption $label """ - end end diff --git a/src/outputs/makie.jl b/src/outputs/makie.jl index e48b16ef..6f1407f7 100644 --- a/src/outputs/makie.jl +++ b/src/outputs/makie.jl @@ -3,19 +3,11 @@ using CairoMakie import Makie -function convert_output(expr, path, p::Makie.FigureAxisPlot; caption=nothing, label=nothing) +function convert_output(expr, path, p::Makie.FigureAxisPlot; caption=missing, label=missing) im_dir = joinpath(BUILD_DIR, "im") mkpath(im_dir) - if isnothing(expr) - # Not determining some random name here, because it would require cleanups too. - msg = """ - It is not possible to write an image without specifying a path. - Use `Options(p; filename=filename)` where `p` is a Makie.jl plot. - """ - throw(ErrorException(msg)) - end - file = method_name(expr) + file = plotting_filename(expr, path, "Makie.jl") println("Writing plot images for $file") svg_filename = "$file.svg" diff --git a/src/outputs/plots.jl b/src/outputs/plots.jl index 836158d2..2bc71775 100644 --- a/src/outputs/plots.jl +++ b/src/outputs/plots.jl @@ -2,19 +2,11 @@ import Plots -function convert_output(expr, path, p::Plots.Plot; caption=nothing, label=nothing) +function convert_output(expr, path, p::Plots.Plot; caption=missing, label=missing) im_dir = joinpath(BUILD_DIR, "im") mkpath(im_dir) - if isnothing(expr) - # Not determining some random name here, because it would require cleanups too. - msg = """ - It is not possible to write an image without specifying a path or filename. - Use `Options(p; filename=filename)` where `p` is a Plots.jl plot. - """ - throw(ErrorException(msg)) - end - file = method_name(expr) + file = plotting_filename(expr, path, "Plots.jl") println("Writing plot images for $file") svg_filename = "$file.svg" diff --git a/src/showcode.jl b/src/showcode.jl index e6a5c0cc..69196e87 100644 --- a/src/showcode.jl +++ b/src/showcode.jl @@ -64,7 +64,7 @@ This should be evaluated inside the correct module since it is typically called inside `Core.eval(M, ex)` in `generate.jl`. """ function eval_convert(expr::AbstractString, M) - ex = Meta.parse(expr) + ex = Meta.parse("begin $expr end") out = Core.eval(M, ex) out = convert_output(expr, nothing, out) end