From c3640014c5507388a51167f2686046b048ee8caa Mon Sep 17 00:00:00 2001 From: Manuel Berkemeier Date: Wed, 19 May 2021 10:10:09 +0200 Subject: [PATCH 1/3] issue #144, first draft --- docs/src/outputformats.md | 24 +++++++++++---- src/Literate.jl | 11 ++++--- test/runtests.jl | 64 +++++++++++++++++++-------------------- 3 files changed, 56 insertions(+), 43 deletions(-) diff --git a/docs/src/outputformats.md b/docs/src/outputformats.md index 939b861c..cac3d138 100644 --- a/docs/src/outputformats.md +++ b/docs/src/outputformats.md @@ -18,7 +18,7 @@ of the source snippet above is as follows: ```@eval import Markdown file = joinpath(@__DIR__, "../src/generated/name.md") -str = "````markdown\n" * rstrip(read(file, String)) * "\n````" +str = "`````markdown\n" * rstrip(read(file, String)) * "\n`````" rm(file) Markdown.parse(str) ``` @@ -29,17 +29,29 @@ an `@meta` block have been added, that sets the `EditURL` variable. This is used by Documenter to redirect the "Edit on GitHub" link for the page, see [Interaction with Documenter](@ref). +The `@example` blocks are wrapped in 4 consecutive backticks so as to allow for docstrings containing triple backticks, for example: +````julia +""" +This function perform the following calculation: +```math + x_1 + x_2 +``` +""" +f(x) = x[1] + x[2] +```` +If your Julia code itself contains 4 consecutive backticks, you can use the keyword argument `codefence` to setup 5 backticks for code blocks, see [Configuration](@ref). + It possible to configure `Literate.markdown` to also evaluate code snippets, capture the result and include it in the output, by passing `execute=true` as a keyword argument. The result of the first code-block in the example above would then become -````markdown -```julia +`````markdown +````julia x = 1//3 -``` -``` +```` +```` 1//3 -``` ```` +````` In this example the output is just plain text. However, if the resulting value of the code block can be displayed as an image (png or jpeg) Literate will include the image diff --git a/src/Literate.jl b/src/Literate.jl index bb6ef162..70a54e58 100644 --- a/src/Literate.jl +++ b/src/Literate.jl @@ -225,7 +225,7 @@ function create_configuration(inputfile; user_config, user_kwargs, type=nothing) cfg["keep_comments"] = false cfg["execute"] = type === :md ? false : true cfg["codefence"] = get(user_config, "documenter", true) && !get(user_config, "execute", cfg["execute"]) ? - ("```@example $(get(user_config, "name", cfg["name"]))" => "```") : ("```julia" => "```") + ("````@example $(get(user_config, "name", cfg["name"]))" => "````") : ("````julia" => "````") # Guess the package (or repository) root url edit_commit = "master" # TODO: Make this configurable like Documenter? deploy_branch = "gh-pages" # TODO: Make this configurable like Documenter? @@ -296,7 +296,7 @@ See the manual section about [Configuration](@ref) for more information. | `credit` | Boolean for controlling the addition of `This file was generated with Literate.jl ...` to the bottom of the page. If you find Literate.jl useful then feel free to keep this. | `true` | | | `keep_comments` | When `true`, keeps markdown lines as comments in the output script. | `false` | Only applicable for `Literate.script`. | | `execute` | Whether to execute and capture the output. | `true` (notebook), `false` (markdown) | Only applicable for `Literate.notebook` and `Literate.markdown`. For markdown this requires at least Literate 2.4. | -| `codefence` | Pair containing opening and closing fence for wrapping code blocks. | `````"```julia" => "```"````` | If `documenter` is `true` the default is `````"```@example"=>"```"`````. | +| `codefence` | Pair containing opening and closing fence for wrapping code blocks. | `````"````julia" => "````"````` | If `documenter` is `true` the default is `````"````@example"=>"````"`````. | | `devurl` | URL for "in-development" docs. | `"dev"` | See [Documenter docs](https://juliadocs.github.io/Documenter.jl/). Unused if `repo_root_url`/`nbviewer_root_url`/`binder_root_url` are set. | | `repo_root_url` | URL to the root of the repository. | - | Determined automatically on Travis CI, GitHub Actions and GitLab CI. Used for `@__REPO_ROOT_URL__`. | | `nbviewer_root_url` | URL to the root of the repository as seen on nbviewer. | - | Determined automatically on Travis CI, GitHub Actions and GitLab CI. Used for `@__NBVIEWER_ROOT_URL__`. | @@ -440,8 +440,9 @@ function markdown(inputfile, outputdir=pwd(); config::Dict=Dict(), kwargs...) iocode = IOBuffer() codefence = config["codefence"]::Pair write(iocode, codefence.first) - # make sure the code block is finalized if we are printing to ```@example - if chunk.continued && startswith(codefence.first, "```@example") && config["documenter"]::Bool + # make sure the code block is finalized if we are printing to ```@example + # (or ````@example, any number of backticks >= 3 works) + if chunk.continued && startswith(codefence.first, r"`{3,}@example") && config["documenter"]::Bool write(iocode, "; continued = true") end write(iocode, '\n') @@ -475,7 +476,7 @@ end function execute_markdown!(io::IO, sb::Module, block::String, outputdir) # TODO: Deal with explicit display(...) calls r, str, _ = execute_block(sb, block) - plain_fence = "\n```\n" => "\n```" # issue #101: consecutive codefenced blocks need newline + plain_fence = "\n````\n" => "\n````" # issue #101: consecutive codefenced blocks need newline; issue #144: quadruple backticks allow for tripple backticks in the output - won't probably ever occur, but better safe than sorry if r !== nothing && !REPL.ends_with_semicolon(block) for (mime, ext) in [(MIME("image/png"), ".png"), (MIME("image/jpeg"), ".jpeg")] if showable(mime, r) diff --git a/test/runtests.jl b/test/runtests.jl index 08546716..7c2de9cc 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -467,66 +467,66 @@ end end # [Example](@id example-id) [foo](@ref), [bar](@ref bbaarr) - ```@example inputfile + ````@example inputfile x = 1 - ``` + ```` Only markdown Only markdown - ```@example inputfile + ````@example inputfile x + 1 x + 1 - ``` + ```` Not notebook Not notebook - ```@example inputfile + ````@example inputfile x * 2 x * 2 - ``` + ```` Not script Not script - ```@example inputfile + ````@example inputfile x * 3 x * 3 # # Comment # another comment - ``` + ```` - ```@example inputfile; continued = true + ````@example inputfile; continued = true for i in 1:10 print(i) - ``` + ```` some markdown in a code block - ```@example inputfile + ````@example inputfile end - ``` + ```` name: inputfile Link to repo root: https://github.com/fredrikekre/Literate.jl/blob/master/file.jl Link to nbviewer: https://nbviewer.jupyter.org/github/fredrikekre/Literate.jl/blob/gh-pages/v1.2.0/file.jl Link to binder: https://mybinder.org/v2/gh/fredrikekre/Literate.jl/gh-pages?filepath=v1.2.0/file.jl - ```@example inputfile + ````@example inputfile # name: inputfile # Link to repo root: https://github.com/fredrikekre/Literate.jl/blob/master/file.jl # Link to nbviewer: https://nbviewer.jupyter.org/github/fredrikekre/Literate.jl/blob/gh-pages/v1.2.0/file.jl # Link to binder: https://mybinder.org/v2/gh/fredrikekre/Literate.jl/gh-pages?filepath=v1.2.0/file.jl - ``` + ```` PLACEHOLDER1 PLACEHOLDER2 - ```@example inputfile + ````@example inputfile # PLACEHOLDER3 # PLACEHOLDER4 - ``` + ```` Some math: ```math @@ -535,37 +535,37 @@ end end Indented markdown - ```@example inputfile; continued = true + ````@example inputfile; continued = true for i in 1:10 - ``` + ```` Indented markdown - ```@example inputfile + ````@example inputfile # Indented comment end - ``` + ```` Semicolon output supression - ```@example inputfile + ````@example inputfile 1 + 1; nothing #hide - ``` + ```` Completely hidden - ```@example inputfile + ````@example inputfile hidden = 12 #hide hidden * hidden #hide - ``` + ```` Partially hidden - ```@example inputfile + ````@example inputfile hidden2 = 12 #hide hidden2 * hidden2 - ``` + ```` First multiline comment @@ -673,7 +673,7 @@ end end Literate.markdown(inputfile, outdir, documenter = false) markdown = read(joinpath(outdir, "inputfile.md"), String) @test occursin("```julia", markdown) - @test !occursin("```@example", markdown) + @test !occursin(r"`{3,}@example", markdown) @test !occursin("continued = true", markdown) @test !occursin("EditURL", markdown) @test !occursin("#hide", markdown) @@ -682,14 +682,14 @@ end end Literate.markdown(inputfile, outdir, codefence = "```c" => "```") markdown = read(joinpath(outdir, "inputfile.md"), String) @test occursin("```c", markdown) - @test !occursin("```@example", markdown) + @test !occursin(r"`{3,}@example", markdown) @test !occursin("```julia", markdown) # name Literate.markdown(inputfile, outdir, name = "foobar") markdown = read(joinpath(outdir, "foobar.md"), String) - @test occursin("```@example foobar", markdown) - @test !occursin("```@example inputfile", markdown) + @test occursin(r"`{3,}@example foobar", markdown) + @test !occursin(r"`{3,}@example inputfile", markdown) @test occursin("name: foobar", markdown) @test !occursin("name: inputfile", markdown) @test !occursin("name: @__NAME__", markdown) @@ -1180,10 +1180,10 @@ end end create(; kw...) = Literate.create_configuration(inputfile; user_config=Dict(), user_kwargs=kw) cfg = create(; type=:md, execute=true) @test cfg["execute"] - @test cfg["codefence"] == ("```julia" => "```") + @test cfg["codefence"] == ("````julia" => "````") cfg = create(; type=:md, execute=false) @test !cfg["execute"] - @test cfg["codefence"] == ("```@example inputfile" => "```") + @test cfg["codefence"] == ("````@example inputfile" => "````") end end end end From dc8fba024a42865f7f02def6c64846749abe690e Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Thu, 8 Jul 2021 12:29:08 +0200 Subject: [PATCH 2/3] Update src/Literate.jl --- src/Literate.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Literate.jl b/src/Literate.jl index 70a54e58..6bb15a7d 100644 --- a/src/Literate.jl +++ b/src/Literate.jl @@ -440,7 +440,7 @@ function markdown(inputfile, outputdir=pwd(); config::Dict=Dict(), kwargs...) iocode = IOBuffer() codefence = config["codefence"]::Pair write(iocode, codefence.first) - # make sure the code block is finalized if we are printing to ```@example + # make sure the code block is finalized if we are printing to ```@example # (or ````@example, any number of backticks >= 3 works) if chunk.continued && startswith(codefence.first, r"`{3,}@example") && config["documenter"]::Bool write(iocode, "; continued = true") From 751650163e76b059921122df6a34ae8e0a9ab9c3 Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Thu, 8 Jul 2021 12:38:53 +0200 Subject: [PATCH 3/3] Fix usage of startswith on julia 1.0. --- src/Literate.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Literate.jl b/src/Literate.jl index be93f9fe..7870b282 100644 --- a/src/Literate.jl +++ b/src/Literate.jl @@ -442,7 +442,7 @@ function markdown(inputfile, outputdir=pwd(); config::Dict=Dict(), kwargs...) write(iocode, codefence.first) # make sure the code block is finalized if we are printing to ```@example # (or ````@example, any number of backticks >= 3 works) - if chunk.continued && startswith(codefence.first, r"`{3,}@example") && config["documenter"]::Bool + if chunk.continued && occursin(r"^`{3,}@example", codefence.first) && config["documenter"]::Bool write(iocode, "; continued = true") end write(iocode, '\n')