From 5d1b4918b9f7b000aef1a094234c4d03539fa574 Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Wed, 11 Dec 2024 14:51:00 +0100 Subject: [PATCH] Apply Runic formatting (#266) --- .github/workflows/Check.yml | 23 + README.md | 2 +- docs/make.jl | 4 +- docs/src/outputformats.jl | 4 +- examples/README.jl | 2 +- examples/example.jl | 6 +- src/IJulia.jl | 6 +- src/Literate.jl | 227 ++-- test/runtests.jl | 2336 ++++++++++++++++++----------------- 9 files changed, 1342 insertions(+), 1268 deletions(-) create mode 100644 .github/workflows/Check.yml diff --git a/.github/workflows/Check.yml b/.github/workflows/Check.yml new file mode 100644 index 0000000..37742fb --- /dev/null +++ b/.github/workflows/Check.yml @@ -0,0 +1,23 @@ +--- +name: Check +on: + push: + branches: + - 'master' + - 'release-' + tags: + - '*' + pull_request: +jobs: + runic: + name: Runic formatting + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: julia-actions/setup-julia@v2 + with: + version: '1.11' + - uses: julia-actions/cache@v2 + - uses: fredrikekre/runic-action@v1 + with: + version: '1.2' diff --git a/README.md b/README.md index 52967c3..3fefe0a 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ running these commands from the package root of Literate.jl: ````julia using Literate -Literate.markdown("examples/README.jl", "."; flavor=Literate.CommonMarkFlavor()) +Literate.markdown("examples/README.jl", "."; flavor = Literate.CommonMarkFlavor()) ```` ### Related packages diff --git a/docs/make.jl b/docs/make.jl index 195fa82..74d973c 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -5,7 +5,7 @@ if haskey(ENV, "GITHUB_ACTIONS") end deployconfig = Documenter.auto_detect_deploy_system() -Documenter.post_status(deployconfig; type="pending", repo="github.com/fredrikekre/Literate.jl.git") +Documenter.post_status(deployconfig; type = "pending", repo = "github.com/fredrikekre/Literate.jl.git") using Literate using Plots # to not capture precompilation output @@ -14,7 +14,7 @@ EXAMPLE = joinpath(@__DIR__, "..", "examples", "example.jl") OUTPUT = joinpath(@__DIR__, "src/generated") function preprocess(str) - str = replace(str, "x = 123" => "y = 321"; count=1) + str = replace(str, "x = 123" => "y = 321"; count = 1) return str end diff --git a/docs/src/outputformats.jl b/docs/src/outputformats.jl index 9dbdad1..91fb42c 100644 --- a/docs/src/outputformats.jl +++ b/docs/src/outputformats.jl @@ -3,9 +3,9 @@ # In julia rational numbers can be constructed with the `//` operator. # Lets define two rational numbers, `x` and `y`: -x = 1//3 +x = 1 // 3 #- -y = 2//5 +y = 2 // 5 # When adding `x` and `y` together we obtain a new rational number: diff --git a/examples/README.jl b/examples/README.jl index 218ca87..ca516e5 100644 --- a/examples/README.jl +++ b/examples/README.jl @@ -20,7 +20,7 @@ # running these commands from the package root of Literate.jl: using Literate -Literate.markdown("examples/README.jl", "."; flavor=Literate.CommonMarkFlavor()) +Literate.markdown("examples/README.jl", "."; flavor = Literate.CommonMarkFlavor()) # ### Related packages diff --git a/examples/example.jl b/examples/example.jl index 6423481..6b61d8b 100644 --- a/examples/example.jl +++ b/examples/example.jl @@ -33,8 +33,8 @@ # as markdown, and all the other lines are interpreted as code. Here is some code: #nb %% A slide [code] {"slideshow": {"slide_type": "fragment"}} -x = 1//3 -y = 2//5 +x = 1 // 3 +y = 2 // 5 #nb # %% A slide [markdown] {"slideshow": {"slide_type": "subslide"}} # In markdown sections we can use markdown syntax. For example, we can @@ -96,7 +96,7 @@ foo() #nb %% A slide [code] {"slideshow": {"slide_type": "subslide"}} using Plots -x = range(0, stop=6π, length=1000) +x = range(0, stop = 6π, length = 1000) y1 = sin.(x) y2 = cos.(x) plot(x, [y1, y2]) diff --git a/src/IJulia.jl b/src/IJulia.jl index f1fb302..a67d833 100644 --- a/src/IJulia.jl +++ b/src/IJulia.jl @@ -17,7 +17,7 @@ const application_vnd_vegalite_v2 = MIME("application/vnd.vegalite.v2+json") # return a String=>String dictionary of mimetype=>data # for passing to Jupyter display_data and execute_result messages. function display_dict(x) - data = Dict{String,Any}("text/plain" => limitstringmime(text_plain, x)) + data = Dict{String, Any}("text/plain" => limitstringmime(text_plain, x)) if showable(application_vnd_vegalite_v2, x) data[string(application_vnd_vegalite_v2)] = JSON.parse(limitstringmime(application_vnd_vegalite_v2, x)) end @@ -57,14 +57,14 @@ function limitstringmime(mime::MIME, x) if israwtext(mime, x) return String(x) else - show(IOContext(buf, :limit=>true, :color=>true), mime, x) + show(IOContext(buf, :limit => true, :color => true), mime, x) end else b64 = Base64EncodePipe(buf) if isa(x, Vector{UInt8}) write(b64, x) # x assumed to be raw binary data else - show(IOContext(b64, :limit=>true, :color=>true), mime, x) + show(IOContext(b64, :limit => true, :color => true), mime, x) end close(b64) end diff --git a/src/Literate.jl b/src/Literate.jl index 8f5ca2e..2e381fc 100644 --- a/src/Literate.jl +++ b/src/Literate.jl @@ -39,7 +39,7 @@ struct QuartoFlavor <: AbstractFlavor end # Parser abstract type Chunk end struct MDChunk <: Chunk - lines::Vector{Pair{String,String}} # indent and content + lines::Vector{Pair{String, String}} # indent and content end MDChunk() = MDChunk(String[]) mutable struct CodeChunk <: Chunk @@ -133,29 +133,27 @@ function parse(flavor::AbstractFlavor, content; allow_continued = true) return chunks end -function replace_default(content, sym; - config::Dict, - branch = "gh-pages", - commit = "master" - ) - repls = Pair{Any,Any}[] +function replace_default( + content, sym; config::Dict, branch = "gh-pages", commit = "master" + ) + repls = Pair{Any, Any}[] # add some shameless advertisement if config["credit"]::Bool if sym === :jl content *= """ - #- - ## This file was generated using Literate.jl, https://github.com/fredrikekre/Literate.jl - """ + #- + ## This file was generated using Literate.jl, https://github.com/fredrikekre/Literate.jl + """ else content *= """ - #- - # --- - # - # *This $(sym === :md ? "page" : "notebook") was generated using [Literate.jl](https://github.com/fredrikekre/Literate.jl).* - """ + #- + # --- + # + # *This $(sym === :md ? "page" : "notebook") was generated using [Literate.jl](https://github.com/fredrikekre/Literate.jl).* + """ end end @@ -168,7 +166,7 @@ function replace_default(content, sym; newlines = sprint() do io foreach(l -> println(io, "# ", l), eachline(IOBuffer(m[1]))) end - str = replace(str, multiline_r => chop(newlines); count=1) + str = replace(str, multiline_r => chop(newlines); count = 1) end return str end @@ -229,7 +227,7 @@ function replace_default(content, sym; push!(repls, r"\[([^]]+?)\]\(@id .*?\)"s => s"\1") # [foo](@id bar) => foo # Convert Documenter admonitions to markdown quotes r = r"^# !!! (?\w+)(?: \"(?.+)\")?(?<lines>(\v^# .*$)+)"m - adm_to_quote = function(s) + adm_to_quote = function (s) m = match(r, s)::RegexMatch io = IOBuffer() print(io, "# > **") @@ -259,11 +257,13 @@ end filename(str) = first(splitext(last(splitdir(str)))) isdocumenter(cfg) = cfg["flavor"]::AbstractFlavor isa DocumenterFlavor -_DEFAULT_IMAGE_FORMATS = [(MIME("image/svg+xml"), ".svg"), (MIME("image/png"), ".png"), - (MIME("image/jpeg"), ".jpeg")] +_DEFAULT_IMAGE_FORMATS = [ + (MIME("image/svg+xml"), ".svg"), (MIME("image/png"), ".png"), + (MIME("image/jpeg"), ".jpeg"), +] # Cache of inputfile => head branch -const HEAD_BRANCH_CACHE = Dict{String,String}() +const HEAD_BRANCH_CACHE = Dict{String, String}() # Guess the package (or repository) root url with "master" as fallback # see JuliaDocs/Documenter.jl#1751 @@ -277,8 +277,8 @@ function edit_commit(inputfile, user_config) git_root = try readchomp( pipeline( - setenv(`$(git) rev-parse --show-toplevel`; dir=dirname(inputfile)); - stderr=devnull, + setenv(`$(git) rev-parse --show-toplevel`; dir = dirname(inputfile)); + stderr = devnull, ) ) catch @@ -298,8 +298,8 @@ function edit_commit(inputfile, user_config) str = try read( pipeline( - setenv(`$(git) remote show origin`, env; dir=dirname(inputfile)), - stderr=devnull, + setenv(`$(git) remote show origin`, env; dir = dirname(inputfile)), + stderr = devnull, ), String, ) @@ -332,30 +332,38 @@ function pick_codefence(::QuartoFlavor, execute::Bool, name::AbstractString) return "```{julia}" => "```" end -function create_configuration(inputfile; user_config, user_kwargs, type=nothing) +function create_configuration(inputfile; user_config, user_kwargs, type = nothing) # Combine user config with user kwargs - user_config = Dict{String,Any}(string(k) => v for (k, v) in user_config) - user_kwargs = Dict{String,Any}(string(k) => v for (k, v) in user_kwargs) + user_config = Dict{String, Any}(string(k) => v for (k, v) in user_config) + user_kwargs = Dict{String, Any}(string(k) => v for (k, v) in user_kwargs) user_config = merge!(user_config, user_kwargs) # deprecation of documenter kwarg if (d = get(user_config, "documenter", nothing); d !== nothing) if type === :md - Base.depwarn("The documenter=$(d) keyword to Literate.markdown is deprecated." * - " Pass `flavor = Literate.$(d ? "DocumenterFlavor" : "CommonMarkFlavor")()`" * - " instead.", Symbol("Literate.markdown")) + Base.depwarn( + "The documenter=$(d) keyword to Literate.markdown is deprecated." * + " Pass `flavor = Literate.$(d ? "DocumenterFlavor" : "CommonMarkFlavor")()`" * + " instead.", Symbol("Literate.markdown") + ) user_config["flavor"] = d ? DocumenterFlavor() : CommonMarkFlavor() elseif type === :nb - Base.depwarn("The documenter=$(d) keyword to Literate.notebook is deprecated." * - " It is not used anymore for notebook output.", Symbol("Literate.notebook")) + Base.depwarn( + "The documenter=$(d) keyword to Literate.notebook is deprecated." * + " It is not used anymore for notebook output.", + Symbol("Literate.notebook") + ) elseif type === :jl - Base.depwarn("The documenter=$(d) keyword to Literate.script is deprecated." * - " It is not used anymore for script output.", Symbol("Literate.script")) + Base.depwarn( + "The documenter=$(d) keyword to Literate.script is deprecated." * + " It is not used anymore for script output.", + Symbol("Literate.script") + ) end end # Add default config - cfg = Dict{String,Any}() + cfg = Dict{String, Any}() cfg["name"] = filename(inputfile) cfg["preprocess"] = identity cfg["postprocess"] = identity @@ -419,7 +427,7 @@ function create_configuration(inputfile; user_config, user_kwargs, type=nothing) cfg["repo_root_url"] = "$(url)/blob/$(cfg["edit_commit"])" end if (url = get(ENV, "CI_PAGES_URL", nothing)) !== nothing && - (m = match(r"https://(.+)", url)) !== nothing + (m = match(r"https://(.+)", url)) !== nothing cfg["nbviewer_root_url"] = "https://nbviewer.jupyter.org/urls/$(m[1])" end end @@ -479,12 +487,13 @@ Available options: `$(_DEFAULT_IMAGE_FORMATS)`. Results which are `showable` with a MIME type are saved with the first match, with the corresponding extension. """ -const DEFAULT_CONFIGURATION=nothing # Dummy const for documentation +const DEFAULT_CONFIGURATION = nothing # Dummy const for documentation function preprocessor(inputfile, outputdir; user_config, user_kwargs, type) # Create configuration by merging default and userdefined - config = create_configuration(inputfile; user_config=user_config, - user_kwargs=user_kwargs, type=type) + config = create_configuration( + inputfile; user_config = user_config, user_kwargs = user_kwargs, type = type + ) # Quarto output does not support execute = true if config["flavor"] isa QuartoFlavor && config["execute"] @@ -505,8 +514,8 @@ function preprocessor(inputfile, outputdir; user_config, user_kwargs, type) end output_thing = type === (:md) ? "markdown page" : - type === (:nb) ? "notebook" : - type === (:jl) ? "plain script file" : error("nope") + type === (:nb) ? "notebook" : + type === (:jl) ? "plain script file" : error("nope") @info "generating $(output_thing) from `$(Base.contractuser(inputfile))`" # Add some information for passing around Literate methods @@ -526,16 +535,17 @@ function preprocessor(inputfile, outputdir; user_config, user_kwargs, type) # change the Edit on GitHub link edit_url = relpath(inputfile, config["literate_outputdir"]) edit_url = replace(edit_url, "\\" => "/") - content = """ + meta_block = """ # ```@meta # EditURL = "$(edit_url)" # ``` - """ * content + """ + content = meta_block * content end # default replacements - content = replace_default(content, type; config=config) + content = replace_default(content, type; config = config) # parse the content into chunks chunks = parse(config["flavor"], content; allow_continued = type !== :nb) @@ -543,7 +553,7 @@ function preprocessor(inputfile, outputdir; user_config, user_kwargs, type) return chunks, config end -function write_result(content, config; print=print) +function write_result(content, config; print = print) outputfile = config["literate_outputfile"] @info "writing result to `$(Base.contractuser(outputfile))`" open(outputfile, "w") do io @@ -560,10 +570,10 @@ Generate a plain script file from `inputfile` and write the result to `outputdir See the manual section on [Configuration](@ref) for documentation of possible configuration with `config` and other keyword arguments. """ -function script(inputfile, outputdir=pwd(); config::AbstractDict=Dict(), kwargs...) +function script(inputfile, outputdir = pwd(); config::AbstractDict = Dict(), kwargs...) # preprocessing and parsing chunks, config = - preprocessor(inputfile, outputdir; user_config=config, user_kwargs=kwargs, type=:jl) + preprocessor(inputfile, outputdir; user_config = config, user_kwargs = kwargs, type = :jl) # create the script file ioscript = IOBuffer() @@ -600,10 +610,10 @@ to the directory `outputdir`. See the manual section on [Configuration](@ref) for documentation of possible configuration with `config` and other keyword arguments. """ -function markdown(inputfile, outputdir=pwd(); config::AbstractDict=Dict(), kwargs...) +function markdown(inputfile, outputdir = pwd(); config::AbstractDict = Dict(), kwargs...) # preprocessing and parsing chunks, config = - preprocessor(inputfile, outputdir; user_config=config, user_kwargs=kwargs, type=:md) + preprocessor(inputfile, outputdir; user_config = config, user_kwargs = kwargs, type = :md) # create the markdown file sb = sandbox() @@ -637,15 +647,16 @@ function markdown(inputfile, outputdir=pwd(); config::AbstractDict=Dict(), kwarg any(write_line, chunk.lines) && write(iomd, seekstart(iocode)) if execute cd(config["literate_outputdir"]) do - execute_markdown!(iomd, sb, join(chunk.lines, '\n'), - config["literate_outputdir"]; - inputfile=config["literate_inputfile"], - fake_source=config["literate_outputfile"], - flavor=config["flavor"], - image_formats=config["image_formats"], - file_prefix="$(config["name"])-$(chunknum)", - softscope=config["softscope"], - continue_on_error=config["continue_on_error"], + execute_markdown!( + iomd, sb, join(chunk.lines, '\n'), + config["literate_outputdir"]; + inputfile = config["literate_inputfile"], + fake_source = config["literate_outputfile"], + flavor = config["flavor"], + image_formats = config["image_formats"], + file_prefix = "$(config["name"])-$(chunknum)", + softscope = config["softscope"], + continue_on_error = config["continue_on_error"], ) end end @@ -661,19 +672,23 @@ function markdown(inputfile, outputdir=pwd(); config::AbstractDict=Dict(), kwarg return outputfile end -function execute_markdown!(io::IO, sb::Module, block::String, outputdir; - inputfile::String, fake_source::String, - flavor::AbstractFlavor, image_formats::Vector, file_prefix::String, - softscope::Bool, continue_on_error::Bool) +function execute_markdown!( + io::IO, sb::Module, block::String, outputdir; + inputfile::String, fake_source::String, flavor::AbstractFlavor, + image_formats::Vector, file_prefix::String, softscope::Bool, + continue_on_error::Bool + ) # TODO: Deal with explicit display(...) calls - r, str, _ = execute_block(sb, block; inputfile=inputfile, fake_source=fake_source, - softscope=softscope, continue_on_error=continue_on_error) + r, str, _ = execute_block( + sb, block; inputfile = inputfile, fake_source = fake_source, + softscope = softscope, continue_on_error = continue_on_error + ) # issue #101: consecutive codefenced blocks need newline # issue #144: quadruple backticks allow for triple backticks in the output - plain_fence = "\n````\n" => "\n````" + plain_fence = "\n````\n" => "\n````" if r !== nothing && !REPL.ends_with_semicolon(block) if (flavor isa FranklinFlavor || flavor isa DocumenterFlavor) && - Base.invokelatest(showable, MIME("text/html"), r) + Base.invokelatest(showable, MIME("text/html"), r) htmlfence = flavor isa FranklinFlavor ? ("~~~" => "~~~") : ("```@raw html" => "```") write(io, "\n", htmlfence.first, "\n") Base.invokelatest(show, io, MIME("text/html"), r) @@ -731,16 +746,16 @@ Generate a notebook from `inputfile` and write the result to `outputdir`. See the manual section on [Configuration](@ref) for documentation of possible configuration with `config` and other keyword arguments. """ -function notebook(inputfile, outputdir=pwd(); config::AbstractDict=Dict(), kwargs...) +function notebook(inputfile, outputdir = pwd(); config::AbstractDict = Dict(), kwargs...) # preprocessing and parsing chunks, config = - preprocessor(inputfile, outputdir; user_config=config, user_kwargs=kwargs, type=:nb) + preprocessor(inputfile, outputdir; user_config = config, user_kwargs = kwargs, type = :nb) # create the notebook nb = jupyter_notebook(chunks, config) # write to file - outputfile = write_result(nb, config; print = (io, c)->JSON.print(io, c, 1)) + outputfile = write_result(nb, config; print = (io, c) -> JSON.print(io, c, 1)) return outputfile end @@ -761,10 +776,12 @@ function jupyter_notebook(chunks, config) else metadata = Dict{String, Any}() end - lines = isa(chunk, MDChunk) ? - String[x.second for x in chunk.lines] : # skip indent - chunk.lines - @views map!(x -> x * '\n', lines[1:end-1], lines[1:end-1]) + if isa(chunk, MDChunk) + lines = String[x.second for x in chunk.lines] # skip indent + else + lines = chunk.lines + end + @views map!(x -> x * '\n', lines[1:(end - 1)], lines[1:(end - 1)]) cell["cell_type"] = chunktype cell["metadata"] = metadata cell["source"] = lines @@ -780,15 +797,15 @@ function jupyter_notebook(chunks, config) metadata = Dict() kernelspec = Dict() - kernelspec["language"] = "julia" - kernelspec["name"] = "julia-$(VERSION.major).$(VERSION.minor)" + kernelspec["language"] = "julia" + kernelspec["name"] = "julia-$(VERSION.major).$(VERSION.minor)" kernelspec["display_name"] = "Julia $(string(VERSION))" metadata["kernelspec"] = kernelspec language_info = Dict() language_info["file_extension"] = ".jl" language_info["mimetype"] = "application/julia" - language_info["name"]= "julia" + language_info["name"] = "julia" language_info["version"] = string(VERSION) metadata["language_info"] = language_info @@ -801,23 +818,26 @@ function jupyter_notebook(chunks, config) @info "executing notebook `$(config["name"] * ".ipynb")`" try cd(config["literate_outputdir"]) do - nb = execute_notebook(nb; inputfile=config["literate_inputfile"], - fake_source=config["literate_outputfile"], - softscope=config["softscope"], - continue_on_error=config["continue_on_error"], -) + nb = execute_notebook( + nb; inputfile = config["literate_inputfile"], + fake_source = config["literate_outputfile"], + softscope = config["softscope"], + continue_on_error = config["continue_on_error"], + ) end catch err @error "error when executing notebook based on input file: " * - "`$(Base.contractuser(config["literate_inputfile"]))`" + "`$(Base.contractuser(config["literate_inputfile"]))`" rethrow(err) end end return nb end -function execute_notebook(nb; inputfile::String, fake_source::String, softscope::Bool, - continue_on_error=continue_on_error) +function execute_notebook( + nb; inputfile::String, fake_source::String, softscope::Bool, + continue_on_error = continue_on_error + ) sb = sandbox() execution_count = 0 for cell in nb["cells"] @@ -825,13 +845,14 @@ function execute_notebook(nb; inputfile::String, fake_source::String, softscope: execution_count += 1 cell["execution_count"] = execution_count block = join(cell["source"]) - r, str, display_dicts = execute_block(sb, block; inputfile=inputfile, - fake_source=fake_source, softscope=softscope, - continue_on_error=continue_on_error) + r, str, display_dicts = execute_block( + sb, block; inputfile = inputfile, fake_source = fake_source, + softscope = softscope, continue_on_error = continue_on_error + ) # str should go into stream if !isempty(str) - stream = Dict{String,Any}() + stream = Dict{String, Any}() stream["output_type"] = "stream" stream["name"] = "stdout" stream["text"] = collect(Any, eachline(IOBuffer(String(str)), keep = true)) @@ -852,7 +873,7 @@ function execute_notebook(nb; inputfile::String, fake_source::String, softscope: # Any explicit calls to display(...) for dict in display_dicts - display_data = Dict{String,Any}() + display_data = Dict{String, Any}() display_data["output_type"] = "display_data" display_data["metadata"] = Dict() display_data["data"] = split_mime(dict) @@ -864,7 +885,7 @@ function execute_notebook(nb; inputfile::String, fake_source::String, softscope: # r should go into execute_result if r !== nothing - execute_result = Dict{String,Any}() + execute_result = Dict{String, Any}() execute_result["output_type"] = "execute_result" execute_result["metadata"] = Dict() execute_result["execution_count"] = execution_count @@ -899,7 +920,7 @@ end # TODO: Problematic to accept mime::MIME here? function Base.display(ld::LiterateDisplay, mime::MIME, x) r = Base.invokelatest(IJulia.limitstringmime, mime, x) - display_dicts = Dict{String,Any}(string(mime) => r) + display_dicts = Dict{String, Any}(string(mime) => r) # TODO: IJulia does this part below for unknown mimes # if istextmime(mime) # display_dicts["text/plain"] = r @@ -909,8 +930,10 @@ function Base.display(ld::LiterateDisplay, mime::MIME, x) end # Execute a code-block in a module and capture stdout/stderr and the result -function execute_block(sb::Module, block::String; inputfile::String, fake_source::String, - softscope::Bool, continue_on_error::Bool) +function execute_block( + sb::Module, block::String; inputfile::String, fake_source::String, + softscope::Bool, continue_on_error::Bool + ) @debug """execute_block($sb, block) ``` $(block) @@ -945,14 +968,16 @@ function execute_block(sb::Module, block::String; inputfile::String, fake_source all_output = c.output * "\n\nERROR: " * sprint(showerror, err) return nothing, all_output, disp.data else - error(""" - $(sprint(showerror, c.value)) - when executing the following code block from inputfile `$(Base.contractuser(inputfile))` - - ```julia - $block - ``` - """) + error( + """ + $(sprint(showerror, c.value)) + when executing the following code block from inputfile `$(Base.contractuser(inputfile))` + + ```julia + $block + ``` + """ + ) end end return c.value, c.output, disp.data diff --git a/test/runtests.jl b/test/runtests.jl index 253de1d..2040685 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -23,6 +23,7 @@ function compare_chunks(chunks1, chunks2) @test c1.continued == c2.continued end end + return end @testset "Literate.parse" begin @@ -115,8 +116,8 @@ end expected_chunks = Chunk[ MDChunk(["" => "Line 1"]), CodeChunk(["Line 2"], false), - MDChunk(["" => "Line 3", "" => "","" => "Line 5"]), - CodeChunk(["Line 6", "","Line 8"], false), + MDChunk(["" => "Line 3", "" => "", "" => "Line 5"]), + CodeChunk(["Line 6", "", "Line 8"], false), MDChunk(["" => "Line 9"]), MDChunk(["" => "Line 11"]), CodeChunk(["Line 12"], false), @@ -149,7 +150,7 @@ end CodeChunk(["# Line 73", "#", "# Line 75"], false), CodeChunk([" # Line 77", " #", " # Line 79"], false), MDChunk(["" => "Line 80: Quarto Specific"]), - CodeChunk(["##| Line 81"], false) + CodeChunk(["##| Line 81"], false), ] parsed_chunks = Literate.parse(DefaultFlavor(), content) compare_chunks(parsed_chunks, expected_chunks) @@ -158,8 +159,8 @@ end expected_chunks_quarto = Chunk[ MDChunk(["" => "Line 1"]), CodeChunk(["Line 2"], false), - MDChunk(["" => "Line 3", "" => "","" => "Line 5"]), - CodeChunk(["Line 6", "","Line 8"], false), + MDChunk(["" => "Line 3", "" => "", "" => "Line 5"]), + CodeChunk(["Line 6", "", "Line 8"], false), MDChunk(["" => "Line 9"]), MDChunk(["" => "Line 11"]), CodeChunk(["Line 12"], false), @@ -192,7 +193,7 @@ end CodeChunk(["# Line 73", "#", "# Line 75"], false), CodeChunk([" # Line 77", " #", " # Line 79"], false), MDChunk(["" => "Line 80: Quarto Specific"]), - CodeChunk(["#| Line 81"], false) # parses correctly as code cell command + CodeChunk(["#| Line 81"], false), # parses correctly as code cell command ] parsed_chunks = Literate.parse(QuartoFlavor(), content) compare_chunks(parsed_chunks, expected_chunks_quarto) @@ -202,13 +203,13 @@ end iows = IOBuffer() for c in expected_chunks if isa(c, CodeChunk) - foreach(x-> println(io, x), c.lines) - foreach(x-> println(iows, x, " "), c.lines) + foreach(x -> println(io, x), c.lines) + foreach(x -> println(iows, x, " "), c.lines) else - foreach(x -> println(io, "# ", x), c.lines) + foreach(x -> println(io, "# ", x), c.lines) foreach(x -> println(iows, "# ", x, " "), c.lines) end - println(io, "#-") + println(io, "#-") println(iows, "#-") foreach(x -> println(iows), 1:rand(2:5)) end @@ -218,111 +219,111 @@ end end # testset parser content = """ - # # [Example](@id example-id) - # [foo](@ref), [bar](@ref bbaarr) - # [baz](@extref), [bax](@extref bbaaxx) - x = 1 - #md # Only markdown - # Only markdown #md - #md x + 1 - x + 1 #md - #!md # Not markdown - # Not markdown #!md - #!md x * 1 - x * 1 #!md - #nb # Only notebook - # Only notebook #nb - #nb x + 2 - x + 2 #nb - #!nb # Not notebook - # Not notebook #!nb - #!nb x * 2 - x * 2 #!nb - #jl # Only script - # Only script #jl - #jl x + 3 - x + 3 #jl - #!jl # Not script - # Not script #!jl - #!jl x * 3 - x * 3 #!jl - #src # Source code only - Source code only #src - ## # Comment - ## another comment - #- - for i in 1:10 - print(i) - # some markdown in a code block - #+ - end - # name: @__NAME__ - # Link to repo root: @__REPO_ROOT_URL__/file.jl - # Link to nbviewer: @__NBVIEWER_ROOT_URL__/file.jl - # Link to binder: @__BINDER_ROOT_URL__/file.jl - ## name: @__NAME__ - ## Link to repo root: @__REPO_ROOT_URL__/file.jl - ## Link to nbviewer: @__NBVIEWER_ROOT_URL__/file.jl - ## Link to binder: @__BINDER_ROOT_URL__/file.jl - - # PLACEHOLDER1 - # PLACEHOLDER2 - ## PLACEHOLDER3 - ## PLACEHOLDER4 - - # Some inline math: ``\\frac{df}{dx}``, some multiline inline math: ``y = - # kx + m``, and some display math: - # ```math - # \\int f(x) dx - # ``` - #- - # Indented markdown - for i in 1:10 - # Indented markdown - #+ - ## Indented comment - end +# # [Example](@id example-id) +# [foo](@ref), [bar](@ref bbaarr) +# [baz](@extref), [bax](@extref bbaaxx) +x = 1 +#md # Only markdown +# Only markdown #md +#md x + 1 +x + 1 #md +#!md # Not markdown +# Not markdown #!md +#!md x * 1 +x * 1 #!md +#nb # Only notebook +# Only notebook #nb +#nb x + 2 +x + 2 #nb +#!nb # Not notebook +# Not notebook #!nb +#!nb x * 2 +x * 2 #!nb +#jl # Only script +# Only script #jl +#jl x + 3 +x + 3 #jl +#!jl # Not script +# Not script #!jl +#!jl x * 3 +x * 3 #!jl +#src # Source code only +Source code only #src +## # Comment +## another comment +#- +for i in 1:10 + print(i) +# some markdown in a code block +#+ +end +# name: @__NAME__ +# Link to repo root: @__REPO_ROOT_URL__/file.jl +# Link to nbviewer: @__NBVIEWER_ROOT_URL__/file.jl +# Link to binder: @__BINDER_ROOT_URL__/file.jl +## name: @__NAME__ +## Link to repo root: @__REPO_ROOT_URL__/file.jl +## Link to nbviewer: @__NBVIEWER_ROOT_URL__/file.jl +## Link to binder: @__BINDER_ROOT_URL__/file.jl + +# PLACEHOLDER1 +# PLACEHOLDER2 +## PLACEHOLDER3 +## PLACEHOLDER4 + +# Some inline math: ``\\frac{df}{dx}``, some multiline inline math: ``y = +# kx + m``, and some display math: +# ```math +# \\int f(x) dx +# ``` +#- + # Indented markdown +for i in 1:10 + # Indented markdown +#+ + ## Indented comment +end - # Some inline html - # ```@raw html - # <a href="https://github.com/fredrikekre/Literate.jl">Literate.jl</a> - # ``` - - # Semicolon output supression - 1 + 1; - - # Completely hidden - hidden = 12 #hide - hidden * hidden #hide - - # Partially hidden - hidden2 = 12 #hide - hidden2 * hidden2 - - #nb # A notebook cell with special metadata - #nb %% Meta1 {"meta": "data"} - #nb 1+1 - #nb #- - #nb # A explicit code notebook cell - #nb #- - #nb %% [code] - #nb 1+2 - #nb #- - #nb # %% [markdown] {"meta": "data"} - #nb # # Explicit markdown cell with metadata - - # It can sometimes happen that a text editor line-wraps [a link which shouldn't - # break](@ref bbaarr) - - #= - First multiline - comment - =# - - #======================= - Second multiline comment - =======================# - """ +# Some inline html +# ```@raw html +# <a href="https://github.com/fredrikekre/Literate.jl">Literate.jl</a> +# ``` + +# Semicolon output supression +1 + 1; + +# Completely hidden +hidden = 12 #hide +hidden * hidden #hide + +# Partially hidden +hidden2 = 12 #hide +hidden2 * hidden2 + +#nb # A notebook cell with special metadata +#nb %% Meta1 {"meta": "data"} +#nb 1+1 +#nb #- +#nb # A explicit code notebook cell +#nb #- +#nb %% [code] +#nb 1+2 +#nb #- +#nb # %% [markdown] {"meta": "data"} +#nb # # Explicit markdown cell with metadata + +# It can sometimes happen that a text editor line-wraps [a link which shouldn't +# break](@ref bbaarr) + +#= +First multiline +comment +=# + +#======================= +Second multiline comment +=======================# +""" const TRAVIS_ENV = Dict( "TRAVIS_REPO_SLUG" => "fredrikekre/Literate.jl", @@ -349,159 +350,175 @@ const GITLAB_ENV = Dict( (k => nothing for k in keys(ACTIONS_ENV))..., ) -@testset "Literate.script" begin; Base.CoreLogging.with_logger(Base.CoreLogging.NullLogger()) do - mktempdir(@__DIR__) do sandbox - cd(sandbox) do - # write content to inputfile - inputfile = "inputfile.jl" - write(inputfile, content) - outdir = mktempdir(pwd()) - - # test defaults - withenv(TRAVIS_ENV...) do - Literate.script(inputfile, outdir) - end - expected_script = """ - x = 1 +function with_nullogger_tempdir_cd(f) + Base.CoreLogging.with_logger(Base.CoreLogging.NullLogger()) do + mktempdir(tmp -> cd(f, tmp), @__DIR__) + end + return +end - x * 1 - x * 1 +@testset "Literate.script" begin + with_nullogger_tempdir_cd() do + # write content to inputfile + inputfile = "inputfile.jl" + write(inputfile, content) + outdir = mktempdir(pwd()) - x * 2 - x * 2 + # test defaults + withenv(TRAVIS_ENV...) do + Literate.script(inputfile, outdir) + end + expected_script = """ + x = 1 - x + 3 - x + 3 - # # Comment - # another comment + x * 1 + x * 1 - for i in 1:10 - print(i) + x * 2 + x * 2 - end + x + 3 + x + 3 + # # Comment + # another comment - # 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 + for i in 1:10 + print(i) - # PLACEHOLDER3 - # PLACEHOLDER4 + end - for i in 1:10 + # 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 - # Indented comment - end + # PLACEHOLDER3 + # PLACEHOLDER4 - 1 + 1; + for i in 1:10 - hidden = 12 #hide - hidden * hidden #hide + # Indented comment + end - hidden2 = 12 #hide - hidden2 * hidden2 + 1 + 1; - # This file was generated using Literate.jl, https://github.com/fredrikekre/Literate.jl - """ - script = read(joinpath(outdir, "inputfile.jl"), String) - @test script == expected_script - - # Travis with with PR preview build - withenv(TRAVIS_ENV..., - "TRAVIS_TAG" => "", - "TRAVIS_PULL_REQUEST" => "42") do - Literate.script(inputfile, outdir) - end - script = read(joinpath(outdir, "inputfile.jl"), String) - @test occursin("# Link to repo root: https://github.com/fredrikekre/Literate.jl/blob/master/file.jl", script) - @test occursin("# Link to nbviewer: https://nbviewer.jupyter.org/github/fredrikekre/Literate.jl/blob/gh-pages/previews/PR42/file.jl", script) - @test occursin("# Link to binder: https://mybinder.org/v2/gh/fredrikekre/Literate.jl/gh-pages?filepath=previews/PR42/file.jl", script) - - # Travis with no tag -> dev directory - withenv(TRAVIS_ENV..., - "TRAVIS_TAG" => "") do - Literate.script(inputfile, outdir) - end - script = read(joinpath(outdir, "inputfile.jl"), String) - @test occursin("# Link to repo root: https://github.com/fredrikekre/Literate.jl/blob/master/file.jl", script) - @test occursin("# Link to nbviewer: https://nbviewer.jupyter.org/github/fredrikekre/Literate.jl/blob/gh-pages/dev/file.jl", script) - @test occursin("# Link to binder: https://mybinder.org/v2/gh/fredrikekre/Literate.jl/gh-pages?filepath=dev/file.jl", script) - - # GitHub Actions with a tag - withenv(ACTIONS_ENV...) do - Literate.script(inputfile, outdir) - end - script = read(joinpath(outdir, "inputfile.jl"), String) - @test occursin("# Link to repo root: https://github.com/fredrikekre/Literate.jl/blob/master/file.jl", script) - @test occursin("# Link to nbviewer: https://nbviewer.jupyter.org/github/fredrikekre/Literate.jl/blob/gh-pages/v1.2.0/file.jl", script) - @test occursin("# Link to binder: https://mybinder.org/v2/gh/fredrikekre/Literate.jl/gh-pages?filepath=v1.2.0/file.jl", script) - - # GitHub Actions with PR preview build - withenv(ACTIONS_ENV..., - "GITHUB_EVENT_NAME" => "pull_request", - "GITHUB_REF" => "refs/pull/42/merge") do - Literate.script(inputfile, outdir) - end - script = read(joinpath(outdir, "inputfile.jl"), String) - @test occursin("# Link to repo root: https://github.com/fredrikekre/Literate.jl/blob/master/file.jl", script) - @test occursin("# Link to nbviewer: https://nbviewer.jupyter.org/github/fredrikekre/Literate.jl/blob/gh-pages/previews/PR42/file.jl", script) - @test occursin("# Link to binder: https://mybinder.org/v2/gh/fredrikekre/Literate.jl/gh-pages?filepath=previews/PR42/file.jl", script) - - # GitHub Actions without a tag -> dev directory - withenv(ACTIONS_ENV..., - "GITHUB_REF" => "refs/heads/master") do - Literate.script(inputfile, outdir) - end - script = read(joinpath(outdir, "inputfile.jl"), String) - @test occursin("# Link to repo root: https://github.com/fredrikekre/Literate.jl/blob/master/file.jl", script) - @test occursin("# Link to nbviewer: https://nbviewer.jupyter.org/github/fredrikekre/Literate.jl/blob/gh-pages/dev/file.jl", script) - @test occursin("# Link to binder: https://mybinder.org/v2/gh/fredrikekre/Literate.jl/gh-pages?filepath=dev/file.jl", script) - - # building under DocumentationGenerator.jl - withenv("DOCUMENTATIONGENERATOR" => "true", - "DOCUMENTATIONGENERATOR_BASE_URL" => "pkg.julialang.org/docs/Literate/XPnWG/1.2.0") do - Literate.script(inputfile, outdir) - end - script = read(joinpath(outdir, "inputfile.jl"), String) - @test occursin("jupyter.org/urls/pkg.julialang.org/docs/Literate/XPnWG/1.2.0/file.jl", script) - @test_broken occursin("https://github.com/fredrikekre/Literate.jl/blob/master/file.jl", script) - - # pre- and post-processing - Literate.script(inputfile, outdir, - preprocess = x -> replace(x, "PLACEHOLDER3" => "3REDLOHECALP"), - postprocess = x -> replace(x, "PLACEHOLDER4" => "4REDLOHECALP")) - script = read(joinpath(outdir, "inputfile.jl"), String) - @test !occursin("PLACEHOLDER1", script) - @test !occursin("PLACEHOLDER2", script) - @test !occursin("PLACEHOLDER3", script) - @test !occursin("PLACEHOLDER4", script) - @test occursin("3REDLOHECALP", script) - @test occursin("4REDLOHECALP", script) - - # name - Literate.script(inputfile, outdir, name = "foobar") - script = read(joinpath(outdir, "foobar.jl"), String) - @test occursin("name: foobar", script) - @test !occursin("name: inputfile", script) - @test !occursin("name: @__NAME__", script) - - # keep_comments - Literate.script(inputfile, outdir, keep_comments = true) - script = read(joinpath(outdir, "inputfile.jl"), String) - @test occursin("# # Example", script) - @test occursin("# foo, bar", script) - @test occursin("# \\int f(x) dx", script) - @test occursin("# First multiline", script) - @test occursin("# Second multiline comment", script) - @test occursin( - """ - # It can sometimes happen that a text editor line-wraps a link which shouldn't - # break""", - script) + hidden = 12 #hide + hidden * hidden #hide - # mdstrings - mdstrings_file = "inputfile_mdstrings.jl" - write(mdstrings_file, """ + hidden2 = 12 #hide + hidden2 * hidden2 + + # This file was generated using Literate.jl, https://github.com/fredrikekre/Literate.jl + """ + script = read(joinpath(outdir, "inputfile.jl"), String) + @test script == expected_script + + # Travis with with PR preview build + withenv( + TRAVIS_ENV..., "TRAVIS_TAG" => "", "TRAVIS_PULL_REQUEST" => "42" + ) do + Literate.script(inputfile, outdir) + end + script = read(joinpath(outdir, "inputfile.jl"), String) + @test occursin("# Link to repo root: https://github.com/fredrikekre/Literate.jl/blob/master/file.jl", script) + @test occursin("# Link to nbviewer: https://nbviewer.jupyter.org/github/fredrikekre/Literate.jl/blob/gh-pages/previews/PR42/file.jl", script) + @test occursin("# Link to binder: https://mybinder.org/v2/gh/fredrikekre/Literate.jl/gh-pages?filepath=previews/PR42/file.jl", script) + + # Travis with no tag -> dev directory + withenv( + TRAVIS_ENV..., "TRAVIS_TAG" => "" + ) do + Literate.script(inputfile, outdir) + end + script = read(joinpath(outdir, "inputfile.jl"), String) + @test occursin("# Link to repo root: https://github.com/fredrikekre/Literate.jl/blob/master/file.jl", script) + @test occursin("# Link to nbviewer: https://nbviewer.jupyter.org/github/fredrikekre/Literate.jl/blob/gh-pages/dev/file.jl", script) + @test occursin("# Link to binder: https://mybinder.org/v2/gh/fredrikekre/Literate.jl/gh-pages?filepath=dev/file.jl", script) + + # GitHub Actions with a tag + withenv(ACTIONS_ENV...) do + Literate.script(inputfile, outdir) + end + script = read(joinpath(outdir, "inputfile.jl"), String) + @test occursin("# Link to repo root: https://github.com/fredrikekre/Literate.jl/blob/master/file.jl", script) + @test occursin("# Link to nbviewer: https://nbviewer.jupyter.org/github/fredrikekre/Literate.jl/blob/gh-pages/v1.2.0/file.jl", script) + @test occursin("# Link to binder: https://mybinder.org/v2/gh/fredrikekre/Literate.jl/gh-pages?filepath=v1.2.0/file.jl", script) + + # GitHub Actions with PR preview build + withenv( + ACTIONS_ENV..., + "GITHUB_EVENT_NAME" => "pull_request", "GITHUB_REF" => "refs/pull/42/merge" + ) do + Literate.script(inputfile, outdir) + end + script = read(joinpath(outdir, "inputfile.jl"), String) + @test occursin("# Link to repo root: https://github.com/fredrikekre/Literate.jl/blob/master/file.jl", script) + @test occursin("# Link to nbviewer: https://nbviewer.jupyter.org/github/fredrikekre/Literate.jl/blob/gh-pages/previews/PR42/file.jl", script) + @test occursin("# Link to binder: https://mybinder.org/v2/gh/fredrikekre/Literate.jl/gh-pages?filepath=previews/PR42/file.jl", script) + + # GitHub Actions without a tag -> dev directory + withenv( + ACTIONS_ENV..., "GITHUB_REF" => "refs/heads/master" + ) do + Literate.script(inputfile, outdir) + end + script = read(joinpath(outdir, "inputfile.jl"), String) + @test occursin("# Link to repo root: https://github.com/fredrikekre/Literate.jl/blob/master/file.jl", script) + @test occursin("# Link to nbviewer: https://nbviewer.jupyter.org/github/fredrikekre/Literate.jl/blob/gh-pages/dev/file.jl", script) + @test occursin("# Link to binder: https://mybinder.org/v2/gh/fredrikekre/Literate.jl/gh-pages?filepath=dev/file.jl", script) + + # building under DocumentationGenerator.jl + withenv( + "DOCUMENTATIONGENERATOR" => "true", + "DOCUMENTATIONGENERATOR_BASE_URL" => "pkg.julialang.org/docs/Literate/XPnWG/1.2.0" + ) do + Literate.script(inputfile, outdir) + end + script = read(joinpath(outdir, "inputfile.jl"), String) + @test occursin("jupyter.org/urls/pkg.julialang.org/docs/Literate/XPnWG/1.2.0/file.jl", script) + @test_broken occursin("https://github.com/fredrikekre/Literate.jl/blob/master/file.jl", script) + + # pre- and post-processing + Literate.script( + inputfile, outdir, + preprocess = x -> replace(x, "PLACEHOLDER3" => "3REDLOHECALP"), + postprocess = x -> replace(x, "PLACEHOLDER4" => "4REDLOHECALP") + ) + script = read(joinpath(outdir, "inputfile.jl"), String) + @test !occursin("PLACEHOLDER1", script) + @test !occursin("PLACEHOLDER2", script) + @test !occursin("PLACEHOLDER3", script) + @test !occursin("PLACEHOLDER4", script) + @test occursin("3REDLOHECALP", script) + @test occursin("4REDLOHECALP", script) + + # name + Literate.script(inputfile, outdir, name = "foobar") + script = read(joinpath(outdir, "foobar.jl"), String) + @test occursin("name: foobar", script) + @test !occursin("name: inputfile", script) + @test !occursin("name: @__NAME__", script) + + # keep_comments + Literate.script(inputfile, outdir, keep_comments = true) + script = read(joinpath(outdir, "inputfile.jl"), String) + @test occursin("# # Example", script) + @test occursin("# foo, bar", script) + @test occursin("# \\int f(x) dx", script) + @test occursin("# First multiline", script) + @test occursin("# Second multiline comment", script) + @test occursin( + """ + # It can sometimes happen that a text editor line-wraps a link which shouldn't + # break""", + script + ) + + # mdstrings + mdstrings_file = "inputfile_mdstrings.jl" + write( + mdstrings_file, + """ md\"\"\" # Markdown header @@ -516,11 +533,14 @@ const GITLAB_ENV = Dict( comment ===# 2 + 2 - """) - Literate.script(mdstrings_file, outdir, - keep_comments = true, credit=false) - script = read(joinpath(outdir, mdstrings_file), String) - @test strip(script) == """ + """ + ) + Literate.script( + mdstrings_file, outdir, + keep_comments = true, credit = false + ) + script = read(joinpath(outdir, mdstrings_file), String) + @test strip(script) == """ md\"\"\" # Markdown header @@ -535,10 +555,12 @@ const GITLAB_ENV = Dict( # comment 2 + 2""" - Literate.script(mdstrings_file, outdir, - keep_comments = true, mdstrings = true, credit=false) - script = read(joinpath(outdir, mdstrings_file), String) - @test strip(script) == """ + Literate.script( + mdstrings_file, outdir, + keep_comments = true, mdstrings = true, credit = false + ) + script = read(joinpath(outdir, mdstrings_file), String) + @test strip(script) == """ # # Markdown header # # Content of the multiline markdown @@ -551,796 +573,802 @@ const GITLAB_ENV = Dict( 2 + 2""" - # verify that inputfile exists - @test_throws ArgumentError Literate.script("nonexistent.jl", outdir) + # verify that inputfile exists + @test_throws ArgumentError Literate.script("nonexistent.jl", outdir) - # default output directory - Literate.script(inputfile; name="default-output-directory") - @test isfile("default-output-directory.jl") - @test_throws ArgumentError Literate.script(inputfile) - end + # default output directory + Literate.script(inputfile; name = "default-output-directory") + @test isfile("default-output-directory.jl") + @test_throws ArgumentError Literate.script(inputfile) end -end end - -@testset "Literate.markdown" begin; Base.CoreLogging.with_logger(Base.CoreLogging.NullLogger()) do - mktempdir(@__DIR__) do sandbox - cd(sandbox) do - # write content to inputfile - inputfile = "inputfile.jl" - write(inputfile, content) - outdir = mktempdir(pwd()) - - # test defaults - withenv(TRAVIS_ENV...) do - Literate.markdown(inputfile, outdir) - end - expected_markdown = """ - ```@meta - EditURL = "../inputfile.jl" - ``` - - # [Example](@id example-id) - [foo](@ref), [bar](@ref bbaarr) - [baz](@extref), [bax](@extref bbaaxx) - - ````@example inputfile - x = 1 - ```` - - Only markdown - Only markdown - - ````@example inputfile - x + 1 - x + 1 - ```` - - Not notebook - Not notebook - - ````@example inputfile - x * 2 - x * 2 - ```` - - Not script - Not script - - ````@example inputfile - x * 3 - x * 3 - # # Comment - # another comment - ```` - - ````@example inputfile; continued = true - for i in 1:10 - print(i) - ```` - - some markdown in a code block - - ````@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 +end - ````@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 - ```` +@testset "Literate.markdown" begin + with_nullogger_tempdir_cd() do + # write content to inputfile + inputfile = "inputfile.jl" + write(inputfile, content) + outdir = mktempdir(pwd()) - PLACEHOLDER1 - PLACEHOLDER2 + # test defaults + withenv(TRAVIS_ENV...) do + Literate.markdown(inputfile, outdir) + end + expected_markdown = """ + ```@meta + EditURL = "../inputfile.jl" + ``` - ````@example inputfile - # PLACEHOLDER3 - # PLACEHOLDER4 - ```` + # [Example](@id example-id) + [foo](@ref), [bar](@ref bbaarr) + [baz](@extref), [bax](@extref bbaaxx) - Some inline math: ``\\frac{df}{dx}``, some multiline inline math: ``y = - kx + m``, and some display math: - ```math - \\int f(x) dx - ``` + ````@example inputfile + x = 1 + ```` - Indented markdown + Only markdown + Only markdown - ````@example inputfile; continued = true - for i in 1:10 - ```` + ````@example inputfile + x + 1 + x + 1 + ```` - Indented markdown + Not notebook + Not notebook - ````@example inputfile - # Indented comment - end - ```` + ````@example inputfile + x * 2 + x * 2 + ```` - Some inline html - ```@raw html - <a href="https://github.com/fredrikekre/Literate.jl">Literate.jl</a> - ``` + Not script + Not script - Semicolon output supression + ````@example inputfile + x * 3 + x * 3 + # # Comment + # another comment + ```` - ````@example inputfile - 1 + 1; - nothing #hide - ```` + ````@example inputfile; continued = true + for i in 1:10 + print(i) + ```` - Completely hidden + some markdown in a code block - ````@example inputfile - hidden = 12 #hide - hidden * hidden #hide - ```` + ````@example inputfile + end + ```` - Partially hidden + 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 - hidden2 = 12 #hide - hidden2 * hidden2 - ```` + ````@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 + ```` - It can sometimes happen that a text editor line-wraps [a link which shouldn't - break](@ref bbaarr) - - First multiline - comment + PLACEHOLDER1 + PLACEHOLDER2 - Second multiline comment + ````@example inputfile + # PLACEHOLDER3 + # PLACEHOLDER4 + ```` - --- + Some inline math: ``\\frac{df}{dx}``, some multiline inline math: ``y = + kx + m``, and some display math: + ```math + \\int f(x) dx + ``` - *This page was generated using [Literate.jl](https://github.com/fredrikekre/Literate.jl).* + Indented markdown - """ - markdown = read(joinpath(outdir, "inputfile.md"), String) - @test markdown == expected_markdown - - # Travis with PR preview build - withenv(TRAVIS_ENV..., - "TRAVIS_TAG" => "", - "TRAVIS_PULL_REQUEST" => "42") do - Literate.markdown(inputfile, outdir) - end - markdown = read(joinpath(outdir, "inputfile.md"), String) - @test occursin("Link to repo root: https://github.com/fredrikekre/Literate.jl/blob/master/file.jl", markdown) - @test occursin("Link to nbviewer: https://nbviewer.jupyter.org/github/fredrikekre/Literate.jl/blob/gh-pages/previews/PR42/file.jl", markdown) - @test occursin("Link to binder: https://mybinder.org/v2/gh/fredrikekre/Literate.jl/gh-pages?filepath=previews/PR42/file.jl", markdown) - @test occursin("EditURL = \"../inputfile.jl\"", markdown) - - # Travis with no tag -> dev directory - withenv(TRAVIS_ENV..., - "TRAVIS_TAG" => "") do - Literate.markdown(inputfile, outdir) - end - markdown = read(joinpath(outdir, "inputfile.md"), String) - @test occursin("Link to repo root: https://github.com/fredrikekre/Literate.jl/blob/master/file.jl", markdown) - @test occursin("Link to nbviewer: https://nbviewer.jupyter.org/github/fredrikekre/Literate.jl/blob/gh-pages/dev/file.jl", markdown) - @test occursin("Link to binder: https://mybinder.org/v2/gh/fredrikekre/Literate.jl/gh-pages?filepath=dev/file.jl", markdown) - @test occursin("EditURL = \"../inputfile.jl\"", markdown) - - # GitHub Actions with a tag - withenv(ACTIONS_ENV...) do - Literate.markdown(inputfile, outdir) - end - markdown = read(joinpath(outdir, "inputfile.md"), String) - @test occursin("Link to repo root: https://github.com/fredrikekre/Literate.jl/blob/master/file.jl", markdown) - @test occursin("Link to nbviewer: https://nbviewer.jupyter.org/github/fredrikekre/Literate.jl/blob/gh-pages/v1.2.0/file.jl", markdown) - @test occursin("Link to binder: https://mybinder.org/v2/gh/fredrikekre/Literate.jl/gh-pages?filepath=v1.2.0/file.jl", markdown) - @test occursin("EditURL = \"../inputfile.jl\"", markdown) - - # GitHub Actions with PR preview build - withenv(ACTIONS_ENV..., - "GITHUB_REF" => "refs/pull/42/merge", - "GITHUB_EVENT_NAME" => "pull_request") do - Literate.markdown(inputfile, outdir) - end - markdown = read(joinpath(outdir, "inputfile.md"), String) - @test occursin("Link to repo root: https://github.com/fredrikekre/Literate.jl/blob/master/file.jl", markdown) - @test occursin("Link to nbviewer: https://nbviewer.jupyter.org/github/fredrikekre/Literate.jl/blob/gh-pages/previews/PR42/file.jl", markdown) - @test occursin("Link to binder: https://mybinder.org/v2/gh/fredrikekre/Literate.jl/gh-pages?filepath=previews/PR42/file.jl", markdown) - @test occursin("EditURL = \"../inputfile.jl\"", markdown) - - # GitHub Actions without a tag -> dev directory - withenv(ACTIONS_ENV..., - "GITHUB_REF" => "refs/heads/master") do - Literate.markdown(inputfile, outdir) - end - markdown = read(joinpath(outdir, "inputfile.md"), String) - @test occursin("Link to repo root: https://github.com/fredrikekre/Literate.jl/blob/master/file.jl", markdown) - @test occursin("Link to nbviewer: https://nbviewer.jupyter.org/github/fredrikekre/Literate.jl/blob/gh-pages/dev/file.jl", markdown) - @test occursin("Link to binder: https://mybinder.org/v2/gh/fredrikekre/Literate.jl/gh-pages?filepath=dev/file.jl", markdown) - @test occursin("EditURL = \"../inputfile.jl\"", markdown) - - # GitLab CI with GitLab Pages - withenv(GITLAB_ENV...) do - Literate.markdown(inputfile, outdir) - end - markdown = read(joinpath(outdir, "inputfile.md"), String) - @test occursin("Link to repo root: https://gitlab.com/fredrikekre/Literate.jl/blob/master/file.jl", markdown) - @test occursin("Link to nbviewer: https://nbviewer.jupyter.org/urls/fredrikekre.gitlab.io/Literate.jl/file.jl", markdown) - @test_broken occursin("Link to binder: https://mybinder.org/v2/gh/fredrikekre/Literate.jl/gh-pages?filepath=dev/file.jl", markdown) - @test occursin("EditURL = \"../inputfile.jl\"", markdown) - - # building under DocumentationGenerator.jl - withenv("DOCUMENTATIONGENERATOR" => "true", - "DOCUMENTATIONGENERATOR_BASE_URL" => "pkg.julialang.org/docs/Literate/XPnWG/1.2.0") do - Literate.markdown(inputfile, outdir) - end - markdown = read(joinpath(outdir, "inputfile.md"), String) - @test occursin("jupyter.org/urls/pkg.julialang.org/docs/Literate/XPnWG/1.2.0/file.jl", markdown) - @test_broken occursin("https://github.com/fredrikekre/Literate.jl/blob/master/file.jl", markdown) - - # pre- and post-processing - Literate.markdown(inputfile, outdir, - preprocess = x -> replace(replace(x, "PLACEHOLDER1" => "1REDLOHECALP"), "PLACEHOLDER3" => "3REDLOHECALP"), - postprocess = x -> replace(replace(x, "PLACEHOLDER2" => "2REDLOHECALP"), "PLACEHOLDER4" => "4REDLOHECALP")) - markdown = read(joinpath(outdir, "inputfile.md"), String) - @test !occursin("PLACEHOLDER1", markdown) - @test !occursin("PLACEHOLDER2", markdown) - @test !occursin("PLACEHOLDER3", markdown) - @test !occursin("PLACEHOLDER4", markdown) - @test occursin("1REDLOHECALP", markdown) - @test occursin("2REDLOHECALP", markdown) - @test occursin("3REDLOHECALP", markdown) - @test occursin("4REDLOHECALP", markdown) - - # flavor = CommonMarkFlavor() - Literate.markdown(inputfile, outdir, flavor = Literate.CommonMarkFlavor()) - markdown = read(joinpath(outdir, "inputfile.md"), String) - @test occursin("```julia", markdown) - @test !occursin(r"`{3,}@example", markdown) - @test !occursin("continued = true", markdown) - @test !occursin("EditURL", markdown) - @test !occursin("#hide", markdown) - - # flavor = QuartoFlavor() - # execution of Quarto markdown is not allowed - let expected_error = ArgumentError("QuartoFlavor does not support `execute = true`.") - @test_throws expected_error Literate.markdown("quarto.jl", flavor = Literate.QuartoFlavor(), execute = true) - end - Literate.markdown(inputfile, outdir, flavor = Literate.QuartoFlavor(),execute=false) - markdown = read(joinpath(outdir, "inputfile.qmd"), String) - @test occursin("```{julia}", markdown) - @test !occursin(r"`{3,}@example", markdown) - @test !occursin("continued = true", markdown) - @test !occursin("EditURL", markdown) - @test !occursin("#hide", markdown) - - # documenter = false (deprecated) - @test_deprecated r"The documenter=true keyword to Literate.markdown is deprecated" begin - Literate.markdown(inputfile, outdir, documenter = true) - end - @test_deprecated r"The documenter=false keyword to Literate.markdown is deprecated" begin - Literate.markdown(inputfile, outdir, documenter = false) - end - markdown = read(joinpath(outdir, "inputfile.md"), String) - @test occursin("```julia", markdown) - @test !occursin(r"`{3,}@example", markdown) - - # codefence - Literate.markdown(inputfile, outdir, codefence = "```c" => "```") - markdown = read(joinpath(outdir, "inputfile.md"), String) - @test occursin("```c", 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(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) - - # mdstrings - Literate.markdown(inputfile, outdir, mdstrings = true) - markdown = read(joinpath(outdir, "inputfile.md"), String) - @test !occursin("md\"\"\"", markdown) - - # edit_commit - withenv(ACTIONS_ENV...) do - Literate.markdown(inputfile, outdir; edit_commit="retsam") - end - markdown = read(joinpath(outdir, "inputfile.md"), String) - @test occursin("blob/retsam/", markdown) - @test !occursin("blob/master/", markdown) - - # execute - write(inputfile, """ - using DisplayAs - #- - 1+1 - #- - [1 2; 3 4] - #- - struct PNG end - Base.show(io::IO, mime::MIME"image/png", ::PNG) = print(io, "PNG") - PNG() - #- - struct JPEG end - Base.show(io::IO, mime::MIME"image/jpeg", ::JPEG) = print(io, "JPEG") - JPEG() - #- - struct SVG end - Base.show(io::IO, mime::MIME"image/svg+xml", ::SVG) = print(io, "SVG") - SVG() - #- - struct MD end - Base.show(io::IO, mime::MIME"text/markdown", ::MD) = print(io, "# " * "MD") - Base.show(io::IO, mime::MIME"text/html", ::MD) = - print(io, "<h1>" * "MD" * "</h1>") - #- - DisplayAs.MD(MD()) - #- - DisplayAs.HTML(MD()) - #- - struct Plain end - Base.showable(::MIME, ::Plain) = false - Base.showable(::MIME"text/plain", ::Plain) = true - Base.show(::IO, ::MIME, ::Plain) = error("only plain output supported") - Base.show(io::IO, ::MIME"text/plain", ::Plain) = print(io, "Plain") - Plain() - #- - print("hello"); print(stdout, ", "); print(stderr, "world") - #- - print("hej, världen") - 42 - #- - 123+123; - #- - nothing - #- - print("hello there") - nothing - #- - a = 2 + 2 - print("a: ", a); nothing #hide - #- - 47 #hide - #- - (@__DIR__) == pwd() ? "cwd correct" : "cwd incorrect" - #- - basename(@__FILE__) - """) - Literate.markdown(inputfile, outdir; execute=true) - markdown = read(joinpath(outdir, "inputfile.md"), String) - @test occursin("```\n2\n```", markdown) # text/plain - @test occursin("```\n2×2 $(Matrix{Int}):\n 1 2\n 3 4\n```", markdown) # text/plain - @test occursin(r"!\[\]\(inputfile-5\.png\)", markdown) # image/png - @test occursin(r"!\[\]\(inputfile-6\.jpeg\)", markdown) # image/jpeg - @test occursin(r"!\[\]\(inputfile-7\.svg\)", markdown) # image/svg+xml, fredrikekre/Literate.jl#182 - @test occursin("# MD", markdown) # text/markdown - @test occursin("```@raw html\n<h1>MD</h1>\n```", markdown) # text/html - @test occursin("```\nPlain\n```", markdown) # text/plain, fredrikekre/Literate#187 - @test occursin("```\nhello, world\n```", markdown) # stdout/stderr - @test occursin("```\n42\n```", markdown) # result over stdout/stderr - @test occursin("```julia\n123+123;\n```", markdown) # no additional `nothing #hide`, fredrikekre/Literate.jl/issues/166#issuecomment-979987878 - @test !occursin("246", markdown) # empty output because trailing ; - @test !occursin("```\nnothing\n```", markdown) # empty output because nothing as return value - @test occursin("```\nhello there\n```", markdown) # nothing as return value, non-empty stdout - @test occursin("```julia\na = 2 + 2\n```", markdown) # line with `#hide` removed - @test occursin("```\na: 4\n```", markdown) # nothing as return value, non-empty stdout - @test !occursin("```julia\n47 #hide\n```", markdown) # line with `#hide` removed - @test !occursin("```julia\n```", markdown) # no empty code block - @test occursin("```\n47\n```", markdown) # return value (even though line/block removed) - @test occursin("```\n\"cwd correct\"\n```", markdown) # Correct cwd (@__DIR__) - @test occursin("```\n\"inputfile.md\"\n```", markdown) # Correct source file (@__FILE__) - - # FranklinFlavor - Literate.markdown(inputfile, outdir; execute=true, flavor=Literate.FranklinFlavor()) - markdown = read(joinpath(outdir, "inputfile.md"), String) - @test occursin("# MD", markdown) # text/markdown - @test occursin("~~~\n<h1>MD</h1>\n~~~", markdown) # text/html - - # QuartoFlavor file extension - write(inputfile, "#=\r\nhello world\n=#\r\n") - _, config = Literate.preprocessor(inputfile, outdir; user_kwargs=(), user_config=Dict("flavor"=>Literate.QuartoFlavor()), type=:md) - @test config["literate_ext"] == ".qmd" - - # verify that inputfile exists - @test_throws ArgumentError Literate.markdown("nonexistent.jl", outdir) - - # default output directory - @test !isfile("inputfile.md") - Literate.markdown(inputfile; execute=false) - @test isfile("inputfile.md") - - # fredrikekre/Literate.jl#165: \r\n line endings with multiline comments/mdstrings - write(inputfile, "#=\r\nhello world\r\nhej världen\r\n=#\r\n") - chunks, _ = Literate.preprocessor(inputfile, outdir; user_kwargs=(), user_config=(), type=:md) - @test chunks[2].lines == ["" => "hello world", "" => "hej världen"] - write(inputfile, "md\"\"\"\r\nhello world\r\nhej världen\r\n\"\"\"\r\n") - chunks, _ = Literate.preprocessor(inputfile, outdir; user_kwargs=pairs((; mdstrings=true)), - user_config=(), type=:md) - @test chunks[2].lines == ["" => "hello world", "" => "hej världen"] - - # fredrikekre/Literate.jl#168 - f = "file with space.jl" - write(f, "1 + 1") - Literate.markdown(f, outdir) - @test occursin("file_with_space", read(joinpath(outdir, "file with space.md"), String)) - - # Relative output directory + image output - # https://github.com/fredrikekre/Literate.jl/issues/228 - write( - inputfile, - """ - struct SVG end - Base.show(io::IO, mime::MIME"image/svg+xml", ::SVG) = print(io, "issue228") - SVG() - """, - ) - Literate.markdown(inputfile, relpath(outdir); execute=true, - flavor=Literate.CommonMarkFlavor()) - @test read(joinpath(outdir, "inputfile-1.svg"), String) == "issue228" + ````@example inputfile; continued = true + for i in 1:10 + ```` - # Softscope - write( - inputfile, - """ - ret = 0 - for k = 1:10 - ret += k - end - println("ret = ", ret) - """ - ) - Literate.markdown(inputfile, outdir; execute=true, softscope=true) - @test occursin("ret = 55", read(joinpath(outdir, "inputfile.md"), String)) - ## Disabled softscope - try - Literate.markdown(inputfile, outdir; execute=true, softscope=false) - error("unreachable") - catch err - @test occursin(r"`?ret`? not defined", sprint(Base.showerror, err)) - end - end # cd(sandbox) - end # mktemp -end end + Indented markdown -@testset "Literate.notebook" begin; Base.CoreLogging.with_logger(Base.CoreLogging.NullLogger()) do - mktempdir(@__DIR__) do sandbox - cd(sandbox) do - # write content to inputfile - inputfile = "inputfile.jl" - write(inputfile, content) - outdir = mktempdir(pwd()) - - # test defaults - withenv(TRAVIS_ENV...) do - Literate.notebook(inputfile, outdir, execute = false) - end - expected_cells = rstrip.(( - """ - "cells": [ - """, + ````@example inputfile + # Indented comment + end + ```` - """ - "source": [ - "# Example\\n", - "foo, bar\\n", - "baz, bax" - ] - """, + Some inline html + ```@raw html + <a href="https://github.com/fredrikekre/Literate.jl">Literate.jl</a> + ``` - """ - "source": [ - "x = 1" - ] - """, + Semicolon output supression - """ - "source": [ - "Not markdown\\n", - "Not markdown" - ], - """, + ````@example inputfile + 1 + 1; + nothing #hide + ```` - """ - "source": [ - "x * 1\\n", - "x * 1" - ], - """, + Completely hidden - """ - "source": [ - "Only notebook\\n", - "Only notebook" - ] - """, + ````@example inputfile + hidden = 12 #hide + hidden * hidden #hide + ```` - """ - "source": [ - "x + 2\\n", - "x + 2" - ] - """, + Partially hidden - """ - "source": [ - "Not script\\n", - "Not script" - ], - """, + ````@example inputfile + hidden2 = 12 #hide + hidden2 * hidden2 + ```` - """ - "source": [ - "x * 3\\n", - "x * 3\\n", - "# # Comment\\n", - "# another comment" - ], - """, + It can sometimes happen that a text editor line-wraps [a link which shouldn't + break](@ref bbaarr) - """ - "source": [ - "for i in 1:10\\n", - " print(i)\\n", - "# some markdown in a code block\\n", - "end" - ] - """, + First multiline + comment - """ - "source": [ - "name: inputfile\\n", - "Link to repo root: https://github.com/fredrikekre/Literate.jl/blob/master/file.jl\\n", - "Link to nbviewer: https://nbviewer.jupyter.org/github/fredrikekre/Literate.jl/blob/gh-pages/v1.2.0/file.jl\\n", - "Link to binder: https://mybinder.org/v2/gh/fredrikekre/Literate.jl/gh-pages?filepath=v1.2.0/file.jl" - ] - """, + Second multiline comment - """ - "source": [ - "# name: inputfile\\n", - "# Link to repo root: https://github.com/fredrikekre/Literate.jl/blob/master/file.jl\\n", - "# Link to nbviewer: https://nbviewer.jupyter.org/github/fredrikekre/Literate.jl/blob/gh-pages/v1.2.0/file.jl\\n", - "# Link to binder: https://mybinder.org/v2/gh/fredrikekre/Literate.jl/gh-pages?filepath=v1.2.0/file.jl" - ] - """, + --- - """ - "source": [ - "PLACEHOLDER1\\n", - "PLACEHOLDER2" - ] - """, + *This page was generated using [Literate.jl](https://github.com/fredrikekre/Literate.jl).* + """ + markdown = read(joinpath(outdir, "inputfile.md"), String) + @test markdown == expected_markdown + + # Travis with PR preview build + withenv( + TRAVIS_ENV..., "TRAVIS_TAG" => "", "TRAVIS_PULL_REQUEST" => "42" + ) do + Literate.markdown(inputfile, outdir) + end + markdown = read(joinpath(outdir, "inputfile.md"), String) + @test occursin("Link to repo root: https://github.com/fredrikekre/Literate.jl/blob/master/file.jl", markdown) + @test occursin("Link to nbviewer: https://nbviewer.jupyter.org/github/fredrikekre/Literate.jl/blob/gh-pages/previews/PR42/file.jl", markdown) + @test occursin("Link to binder: https://mybinder.org/v2/gh/fredrikekre/Literate.jl/gh-pages?filepath=previews/PR42/file.jl", markdown) + @test occursin("EditURL = \"../inputfile.jl\"", markdown) + + # Travis with no tag -> dev directory + withenv( + TRAVIS_ENV..., "TRAVIS_TAG" => "" + ) do + Literate.markdown(inputfile, outdir) + end + markdown = read(joinpath(outdir, "inputfile.md"), String) + @test occursin("Link to repo root: https://github.com/fredrikekre/Literate.jl/blob/master/file.jl", markdown) + @test occursin("Link to nbviewer: https://nbviewer.jupyter.org/github/fredrikekre/Literate.jl/blob/gh-pages/dev/file.jl", markdown) + @test occursin("Link to binder: https://mybinder.org/v2/gh/fredrikekre/Literate.jl/gh-pages?filepath=dev/file.jl", markdown) + @test occursin("EditURL = \"../inputfile.jl\"", markdown) + + # GitHub Actions with a tag + withenv(ACTIONS_ENV...) do + Literate.markdown(inputfile, outdir) + end + markdown = read(joinpath(outdir, "inputfile.md"), String) + @test occursin("Link to repo root: https://github.com/fredrikekre/Literate.jl/blob/master/file.jl", markdown) + @test occursin("Link to nbviewer: https://nbviewer.jupyter.org/github/fredrikekre/Literate.jl/blob/gh-pages/v1.2.0/file.jl", markdown) + @test occursin("Link to binder: https://mybinder.org/v2/gh/fredrikekre/Literate.jl/gh-pages?filepath=v1.2.0/file.jl", markdown) + @test occursin("EditURL = \"../inputfile.jl\"", markdown) + + # GitHub Actions with PR preview build + withenv( + ACTIONS_ENV..., + "GITHUB_REF" => "refs/pull/42/merge", "GITHUB_EVENT_NAME" => "pull_request" + ) do + Literate.markdown(inputfile, outdir) + end + markdown = read(joinpath(outdir, "inputfile.md"), String) + @test occursin("Link to repo root: https://github.com/fredrikekre/Literate.jl/blob/master/file.jl", markdown) + @test occursin("Link to nbviewer: https://nbviewer.jupyter.org/github/fredrikekre/Literate.jl/blob/gh-pages/previews/PR42/file.jl", markdown) + @test occursin("Link to binder: https://mybinder.org/v2/gh/fredrikekre/Literate.jl/gh-pages?filepath=previews/PR42/file.jl", markdown) + @test occursin("EditURL = \"../inputfile.jl\"", markdown) + + # GitHub Actions without a tag -> dev directory + withenv( + ACTIONS_ENV..., "GITHUB_REF" => "refs/heads/master" + ) do + Literate.markdown(inputfile, outdir) + end + markdown = read(joinpath(outdir, "inputfile.md"), String) + @test occursin("Link to repo root: https://github.com/fredrikekre/Literate.jl/blob/master/file.jl", markdown) + @test occursin("Link to nbviewer: https://nbviewer.jupyter.org/github/fredrikekre/Literate.jl/blob/gh-pages/dev/file.jl", markdown) + @test occursin("Link to binder: https://mybinder.org/v2/gh/fredrikekre/Literate.jl/gh-pages?filepath=dev/file.jl", markdown) + @test occursin("EditURL = \"../inputfile.jl\"", markdown) + + # GitLab CI with GitLab Pages + withenv(GITLAB_ENV...) do + Literate.markdown(inputfile, outdir) + end + markdown = read(joinpath(outdir, "inputfile.md"), String) + @test occursin("Link to repo root: https://gitlab.com/fredrikekre/Literate.jl/blob/master/file.jl", markdown) + @test occursin("Link to nbviewer: https://nbviewer.jupyter.org/urls/fredrikekre.gitlab.io/Literate.jl/file.jl", markdown) + @test_broken occursin("Link to binder: https://mybinder.org/v2/gh/fredrikekre/Literate.jl/gh-pages?filepath=dev/file.jl", markdown) + @test occursin("EditURL = \"../inputfile.jl\"", markdown) + + # building under DocumentationGenerator.jl + withenv( + "DOCUMENTATIONGENERATOR" => "true", + "DOCUMENTATIONGENERATOR_BASE_URL" => "pkg.julialang.org/docs/Literate/XPnWG/1.2.0" + ) do + Literate.markdown(inputfile, outdir) + end + markdown = read(joinpath(outdir, "inputfile.md"), String) + @test occursin("jupyter.org/urls/pkg.julialang.org/docs/Literate/XPnWG/1.2.0/file.jl", markdown) + @test_broken occursin("https://github.com/fredrikekre/Literate.jl/blob/master/file.jl", markdown) + + # pre- and post-processing + Literate.markdown( + inputfile, outdir, + preprocess = x -> replace(replace(x, "PLACEHOLDER1" => "1REDLOHECALP"), "PLACEHOLDER3" => "3REDLOHECALP"), + postprocess = x -> replace(replace(x, "PLACEHOLDER2" => "2REDLOHECALP"), "PLACEHOLDER4" => "4REDLOHECALP") + ) + markdown = read(joinpath(outdir, "inputfile.md"), String) + @test !occursin("PLACEHOLDER1", markdown) + @test !occursin("PLACEHOLDER2", markdown) + @test !occursin("PLACEHOLDER3", markdown) + @test !occursin("PLACEHOLDER4", markdown) + @test occursin("1REDLOHECALP", markdown) + @test occursin("2REDLOHECALP", markdown) + @test occursin("3REDLOHECALP", markdown) + @test occursin("4REDLOHECALP", markdown) + + # flavor = CommonMarkFlavor() + Literate.markdown(inputfile, outdir, flavor = Literate.CommonMarkFlavor()) + markdown = read(joinpath(outdir, "inputfile.md"), String) + @test occursin("```julia", markdown) + @test !occursin(r"`{3,}@example", markdown) + @test !occursin("continued = true", markdown) + @test !occursin("EditURL", markdown) + @test !occursin("#hide", markdown) + + # flavor = QuartoFlavor() + # execution of Quarto markdown is not allowed + let expected_error = ArgumentError("QuartoFlavor does not support `execute = true`.") + @test_throws expected_error Literate.markdown("quarto.jl", flavor = Literate.QuartoFlavor(), execute = true) + end + Literate.markdown(inputfile, outdir, flavor = Literate.QuartoFlavor(), execute = false) + markdown = read(joinpath(outdir, "inputfile.qmd"), String) + @test occursin("```{julia}", markdown) + @test !occursin(r"`{3,}@example", markdown) + @test !occursin("continued = true", markdown) + @test !occursin("EditURL", markdown) + @test !occursin("#hide", markdown) + + # documenter = false (deprecated) + @test_deprecated r"The documenter=true keyword to Literate.markdown is deprecated" begin + Literate.markdown(inputfile, outdir, documenter = true) + end + @test_deprecated r"The documenter=false keyword to Literate.markdown is deprecated" begin + Literate.markdown(inputfile, outdir, documenter = false) + end + markdown = read(joinpath(outdir, "inputfile.md"), String) + @test occursin("```julia", markdown) + @test !occursin(r"`{3,}@example", markdown) + + # codefence + Literate.markdown(inputfile, outdir, codefence = "```c" => "```") + markdown = read(joinpath(outdir, "inputfile.md"), String) + @test occursin("```c", 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(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) + + # mdstrings + Literate.markdown(inputfile, outdir, mdstrings = true) + markdown = read(joinpath(outdir, "inputfile.md"), String) + @test !occursin("md\"\"\"", markdown) + + # edit_commit + withenv(ACTIONS_ENV...) do + Literate.markdown(inputfile, outdir; edit_commit = "retsam") + end + markdown = read(joinpath(outdir, "inputfile.md"), String) + @test occursin("blob/retsam/", markdown) + @test !occursin("blob/master/", markdown) + + # execute + write( + inputfile, """ + using DisplayAs + #- + 1+1 + #- + [1 2; 3 4] + #- + struct PNG end + Base.show(io::IO, mime::MIME"image/png", ::PNG) = print(io, "PNG") + PNG() + #- + struct JPEG end + Base.show(io::IO, mime::MIME"image/jpeg", ::JPEG) = print(io, "JPEG") + JPEG() + #- + struct SVG end + Base.show(io::IO, mime::MIME"image/svg+xml", ::SVG) = print(io, "SVG") + SVG() + #- + struct MD end + Base.show(io::IO, mime::MIME"text/markdown", ::MD) = print(io, "# " * "MD") + Base.show(io::IO, mime::MIME"text/html", ::MD) = + print(io, "<h1>" * "MD" * "</h1>") + #- + DisplayAs.MD(MD()) + #- + DisplayAs.HTML(MD()) + #- + struct Plain end + Base.showable(::MIME, ::Plain) = false + Base.showable(::MIME"text/plain", ::Plain) = true + Base.show(::IO, ::MIME, ::Plain) = error("only plain output supported") + Base.show(io::IO, ::MIME"text/plain", ::Plain) = print(io, "Plain") + Plain() + #- + print("hello"); print(stdout, ", "); print(stderr, "world") + #- + print("hej, världen") + 42 + #- + 123+123; + #- + nothing + #- + print("hello there") + nothing + #- + a = 2 + 2 + print("a: ", a); nothing #hide + #- + 47 #hide + #- + (@__DIR__) == pwd() ? "cwd correct" : "cwd incorrect" + #- + basename(@__FILE__) """ - "source": [ - "# PLACEHOLDER3\\n", - "# PLACEHOLDER4" - ] - """, - + ) + Literate.markdown(inputfile, outdir; execute = true) + markdown = read(joinpath(outdir, "inputfile.md"), String) + @test occursin("```\n2\n```", markdown) # text/plain + @test occursin("```\n2×2 $(Matrix{Int}):\n 1 2\n 3 4\n```", markdown) # text/plain + @test occursin(r"!\[\]\(inputfile-5\.png\)", markdown) # image/png + @test occursin(r"!\[\]\(inputfile-6\.jpeg\)", markdown) # image/jpeg + @test occursin(r"!\[\]\(inputfile-7\.svg\)", markdown) # image/svg+xml, fredrikekre/Literate.jl#182 + @test occursin("# MD", markdown) # text/markdown + @test occursin("```@raw html\n<h1>MD</h1>\n```", markdown) # text/html + @test occursin("```\nPlain\n```", markdown) # text/plain, fredrikekre/Literate#187 + @test occursin("```\nhello, world\n```", markdown) # stdout/stderr + @test occursin("```\n42\n```", markdown) # result over stdout/stderr + @test occursin("```julia\n123+123;\n```", markdown) # no additional `nothing #hide`, fredrikekre/Literate.jl/issues/166#issuecomment-979987878 + @test !occursin("246", markdown) # empty output because trailing ; + @test !occursin("```\nnothing\n```", markdown) # empty output because nothing as return value + @test occursin("```\nhello there\n```", markdown) # nothing as return value, non-empty stdout + @test occursin("```julia\na = 2 + 2\n```", markdown) # line with `#hide` removed + @test occursin("```\na: 4\n```", markdown) # nothing as return value, non-empty stdout + @test !occursin("```julia\n47 #hide\n```", markdown) # line with `#hide` removed + @test !occursin("```julia\n```", markdown) # no empty code block + @test occursin("```\n47\n```", markdown) # return value (even though line/block removed) + @test occursin("```\n\"cwd correct\"\n```", markdown) # Correct cwd (@__DIR__) + @test occursin("```\n\"inputfile.md\"\n```", markdown) # Correct source file (@__FILE__) + + # FranklinFlavor + Literate.markdown(inputfile, outdir; execute = true, flavor = Literate.FranklinFlavor()) + markdown = read(joinpath(outdir, "inputfile.md"), String) + @test occursin("# MD", markdown) # text/markdown + @test occursin("~~~\n<h1>MD</h1>\n~~~", markdown) # text/html + + # QuartoFlavor file extension + write(inputfile, "#=\r\nhello world\n=#\r\n") + _, config = Literate.preprocessor(inputfile, outdir; user_kwargs = (), user_config = Dict("flavor" => Literate.QuartoFlavor()), type = :md) + @test config["literate_ext"] == ".qmd" + + # verify that inputfile exists + @test_throws ArgumentError Literate.markdown("nonexistent.jl", outdir) + + # default output directory + @test !isfile("inputfile.md") + Literate.markdown(inputfile; execute = false) + @test isfile("inputfile.md") + + # fredrikekre/Literate.jl#165: \r\n line endings with multiline comments/mdstrings + write(inputfile, "#=\r\nhello world\r\nhej världen\r\n=#\r\n") + chunks, _ = Literate.preprocessor(inputfile, outdir; user_kwargs = (), user_config = (), type = :md) + @test chunks[2].lines == ["" => "hello world", "" => "hej världen"] + write(inputfile, "md\"\"\"\r\nhello world\r\nhej världen\r\n\"\"\"\r\n") + chunks, _ = Literate.preprocessor( + inputfile, outdir; user_kwargs = pairs((; mdstrings = true)), + user_config = (), type = :md + ) + @test chunks[2].lines == ["" => "hello world", "" => "hej världen"] + + # fredrikekre/Literate.jl#168 + f = "file with space.jl" + write(f, "1 + 1") + Literate.markdown(f, outdir) + @test occursin("file_with_space", read(joinpath(outdir, "file with space.md"), String)) + + # Relative output directory + image output + # https://github.com/fredrikekre/Literate.jl/issues/228 + write( + inputfile, """ - "source": [ - "Some inline math: \$\\\\frac{df}{dx}\$, some multiline inline math: \$y =\\n", - "kx + m\$, and some display math:\\n", - "\$\$\\n", - "\\\\int f(x) dx\\n", - "\$\$" - ] + struct SVG end + Base.show(io::IO, mime::MIME"image/svg+xml", ::SVG) = print(io, "issue228") + SVG() """, - + ) + Literate.markdown( + inputfile, relpath(outdir); execute = true, + flavor = Literate.CommonMarkFlavor() + ) + @test read(joinpath(outdir, "inputfile-1.svg"), String) == "issue228" + + # Softscope + write( + inputfile, """ - "source": [ - "Indented markdown" - ] - """, - + ret = 0 + for k = 1:10 + ret += k + end + println("ret = ", ret) """ - "source": [ - "for i in 1:10\\n", - " # Indented markdown\\n", - " # Indented comment\\n", - "end" - ] - """, + ) + Literate.markdown(inputfile, outdir; execute = true, softscope = true) + @test occursin("ret = 55", read(joinpath(outdir, "inputfile.md"), String)) + ## Disabled softscope + try + Literate.markdown(inputfile, outdir; execute = true, softscope = false) + error("unreachable") + catch err + @test occursin(r"`?ret`? not defined", sprint(Base.showerror, err)) + end + end +end - """ - "source": [ - "Some inline html\\n", - "\\n", - " <a href=\\"https://github.com/fredrikekre/Literate.jl\\">Literate.jl</a>" - ] - """, +@testset "Literate.notebook" begin + with_nullogger_tempdir_cd() do + # write content to inputfile + inputfile = "inputfile.jl" + write(inputfile, content) + outdir = mktempdir(pwd()) - """ - "metadata": { - "meta": "data" - } - """, + # test defaults + withenv(TRAVIS_ENV...) do + Literate.notebook(inputfile, outdir, execute = false) + end + expected_cells = rstrip.( + [ + """ + "cells": [ + """, + """ + "source": [ + "# Example\\n", + "foo, bar\\n", + "baz, bax" + ] + """, + """ + "source": [ + "x = 1" + ] + """, + """ + "source": [ + "Not markdown\\n", + "Not markdown" + ], + """, + """ + "source": [ + "x * 1\\n", + "x * 1" + ], + """, + """ + "source": [ + "Only notebook\\n", + "Only notebook" + ] + """, + """ + "source": [ + "x + 2\\n", + "x + 2" + ] + """, + """ + "source": [ + "Not script\\n", + "Not script" + ], + """, + """ + "source": [ + "x * 3\\n", + "x * 3\\n", + "# # Comment\\n", + "# another comment" + ], + """, + """ + "source": [ + "for i in 1:10\\n", + " print(i)\\n", + "# some markdown in a code block\\n", + "end" + ] + """, + """ + "source": [ + "name: inputfile\\n", + "Link to repo root: https://github.com/fredrikekre/Literate.jl/blob/master/file.jl\\n", + "Link to nbviewer: https://nbviewer.jupyter.org/github/fredrikekre/Literate.jl/blob/gh-pages/v1.2.0/file.jl\\n", + "Link to binder: https://mybinder.org/v2/gh/fredrikekre/Literate.jl/gh-pages?filepath=v1.2.0/file.jl" + ] + """, + """ + "source": [ + "# name: inputfile\\n", + "# Link to repo root: https://github.com/fredrikekre/Literate.jl/blob/master/file.jl\\n", + "# Link to nbviewer: https://nbviewer.jupyter.org/github/fredrikekre/Literate.jl/blob/gh-pages/v1.2.0/file.jl\\n", + "# Link to binder: https://mybinder.org/v2/gh/fredrikekre/Literate.jl/gh-pages?filepath=v1.2.0/file.jl" + ] + """, + """ + "source": [ + "PLACEHOLDER1\\n", + "PLACEHOLDER2" + ] + """, + """ + "source": [ + "# PLACEHOLDER3\\n", + "# PLACEHOLDER4" + ] + """, + """ + "source": [ + "Some inline math: \$\\\\frac{df}{dx}\$, some multiline inline math: \$y =\\n", + "kx + m\$, and some display math:\\n", + "\$\$\\n", + "\\\\int f(x) dx\\n", + "\$\$" + ] + """, + """ + "source": [ + "Indented markdown" + ] + """, + """ + "source": [ + "for i in 1:10\\n", + " # Indented markdown\\n", + " # Indented comment\\n", + "end" + ] + """, + """ + "source": [ + "Some inline html\\n", + "\\n", + " <a href=\\"https://github.com/fredrikekre/Literate.jl\\">Literate.jl</a>" + ] + """, + """ + "metadata": { + "meta": "data" + } + """, + """ + "source": [ + "First multiline\\n", + "comment" + ] + """, + """ + "source": [ + "---\\n", + "\\n", + "*This notebook was generated using [Literate.jl](https://github.com/fredrikekre/Literate.jl).*" + ] + """, + ] + ) - """ - "source": [ - "First multiline\\n", - "comment" - ] - """, + notebook = read(joinpath(outdir, "inputfile.ipynb"), String) - """ - "source": [ - "---\\n", - "\\n", - "*This notebook was generated using [Literate.jl](https://github.com/fredrikekre/Literate.jl).*" - ] - """)) - - notebook = read(joinpath(outdir, "inputfile.ipynb"), String) - - lastidx = 1 - for cell in expected_cells - idx = findnext(cell, notebook, lastidx) - @test idx !== nothing - lastidx = nextind(notebook, last(idx)) - end - # test some of the required metadata - for metadata in (" \"nbformat\": ", " \"nbformat_minor\": ", " \"metadata\": {", " \"language_info\": {", + lastidx = 1 + for cell in expected_cells + idx = findnext(cell, notebook, lastidx) + @test idx !== nothing + lastidx = nextind(notebook, last(idx)) + end + # test some of the required metadata + for metadata in ( + " \"nbformat\": ", " \"nbformat_minor\": ", " \"metadata\": {", " \"language_info\": {", " \"file_extension\": \".jl\"", " \"mimetype\": \"application/julia\"", " \"name\": \"julia\"", " \"version\": ", " \"kernelspec\": {", - " \"name\": \"julia-", " \"display_name\": \"Julia ", " \"language\": \"julia\"") - @test occursin(metadata, notebook) - end - - # no tag -> latest directory - withenv(TRAVIS_ENV..., - "TRAVIS_TAG" => "") do - Literate.notebook(inputfile, outdir, execute = false) - end - notebook = read(joinpath(outdir, "inputfile.ipynb"), String) - @test occursin("fredrikekre/Literate.jl/blob/gh-pages/dev/", notebook) - - # GitHub Actions with a tag - withenv(ACTIONS_ENV...) do - Literate.notebook(inputfile, outdir, execute = false) - end - notebook = read(joinpath(outdir, "inputfile.ipynb"), String) - @test occursin("fredrikekre/Literate.jl/blob/gh-pages/v1.2.0/", notebook) - - # GitHub Actions with PR preview build - withenv(ACTIONS_ENV..., - "GITHUB_REF" => "refs/pull/42/merge", - "GITHUB_EVENT_NAME" => "pull_request") do - Literate.notebook(inputfile, outdir, execute = false) - end - notebook = read(joinpath(outdir, "inputfile.ipynb"), String) - @test occursin("fredrikekre/Literate.jl/blob/gh-pages/previews/PR42/", notebook) + " \"name\": \"julia-", " \"display_name\": \"Julia ", " \"language\": \"julia\"", + ) + @test occursin(metadata, notebook) + end - # GitHub Actions without a tag - withenv(ACTIONS_ENV..., - "GITHUB_REF" => "refs/heads/master") do - Literate.notebook(inputfile, outdir, execute = false) - end - notebook = read(joinpath(outdir, "inputfile.ipynb"), String) - @test occursin("fredrikekre/Literate.jl/blob/gh-pages/dev/", notebook) + # no tag -> latest directory + withenv( + TRAVIS_ENV..., "TRAVIS_TAG" => "" + ) do + Literate.notebook(inputfile, outdir, execute = false) + end + notebook = read(joinpath(outdir, "inputfile.ipynb"), String) + @test occursin("fredrikekre/Literate.jl/blob/gh-pages/dev/", notebook) - # building under DocumentationGenerator.jl - withenv("DOCUMENTATIONGENERATOR" => "true", - "DOCUMENTATIONGENERATOR_BASE_URL" => "pkg.julialang.org/docs/Literate/XPnWG/1.2.0") do - Literate.notebook(inputfile, outdir, execute = false) - end - notebook = read(joinpath(outdir, "inputfile.ipynb"), String) - @test occursin("jupyter.org/urls/pkg.julialang.org/docs/Literate/XPnWG/1.2.0/file.jl", notebook) - @test_broken occursin("https://github.com/fredrikekre/Literate.jl/blob/master/file.jl", notebook) - - # pre- and post-processing - function post(nb) - for cell in nb["cells"] - for i in eachindex(cell["source"]) - cell["source"][i] = replace(cell["source"][i], "PLACEHOLDER2" => "2REDLOHECALP") - cell["source"][i] = replace(cell["source"][i], "PLACEHOLDER4" => "4REDLOHECALP") - end + # GitHub Actions with a tag + withenv(ACTIONS_ENV...) do + Literate.notebook(inputfile, outdir, execute = false) + end + notebook = read(joinpath(outdir, "inputfile.ipynb"), String) + @test occursin("fredrikekre/Literate.jl/blob/gh-pages/v1.2.0/", notebook) + + # GitHub Actions with PR preview build + withenv( + ACTIONS_ENV..., + "GITHUB_REF" => "refs/pull/42/merge", "GITHUB_EVENT_NAME" => "pull_request" + ) do + Literate.notebook(inputfile, outdir, execute = false) + end + notebook = read(joinpath(outdir, "inputfile.ipynb"), String) + @test occursin("fredrikekre/Literate.jl/blob/gh-pages/previews/PR42/", notebook) + + # GitHub Actions without a tag + withenv( + ACTIONS_ENV..., "GITHUB_REF" => "refs/heads/master" + ) do + Literate.notebook(inputfile, outdir, execute = false) + end + notebook = read(joinpath(outdir, "inputfile.ipynb"), String) + @test occursin("fredrikekre/Literate.jl/blob/gh-pages/dev/", notebook) + + # building under DocumentationGenerator.jl + withenv( + "DOCUMENTATIONGENERATOR" => "true", + "DOCUMENTATIONGENERATOR_BASE_URL" => "pkg.julialang.org/docs/Literate/XPnWG/1.2.0" + ) do + Literate.notebook(inputfile, outdir, execute = false) + end + notebook = read(joinpath(outdir, "inputfile.ipynb"), String) + @test occursin("jupyter.org/urls/pkg.julialang.org/docs/Literate/XPnWG/1.2.0/file.jl", notebook) + @test_broken occursin("https://github.com/fredrikekre/Literate.jl/blob/master/file.jl", notebook) + + # pre- and post-processing + function post(nb) + for cell in nb["cells"] + for i in eachindex(cell["source"]) + cell["source"][i] = replace(cell["source"][i], "PLACEHOLDER2" => "2REDLOHECALP") + cell["source"][i] = replace(cell["source"][i], "PLACEHOLDER4" => "4REDLOHECALP") end - return nb - end - Literate.notebook(inputfile, outdir, execute = false, - preprocess = x -> replace(replace(x, "PLACEHOLDER1" => "1REDLOHECALP"), "PLACEHOLDER3" => "3REDLOHECALP"), - postprocess = post) - notebook = read(joinpath(outdir, "inputfile.ipynb"), String) - @test !occursin("PLACEHOLDER1", notebook) - @test !occursin("PLACEHOLDER2", notebook) - @test !occursin("PLACEHOLDER3", notebook) - @test !occursin("PLACEHOLDER4", notebook) - @test occursin("1REDLOHECALP", notebook) - @test occursin("2REDLOHECALP", notebook) - @test occursin("3REDLOHECALP", notebook) - @test occursin("4REDLOHECALP", notebook) - - # documenter = false (deprecated) - @test_deprecated r"The documenter=false keyword to Literate.notebook is deprecated." begin - Literate.notebook(inputfile, outdir, documenter = false, execute = false) end - notebook = read(joinpath(outdir, "inputfile.ipynb"), String) - @test !occursin("# [Example](@id example-id", notebook) - @test !occursin("[foo](@ref), [bar](@ref bbaarr)", notebook) - @test !occursin("[baz](@ref), [bax](@ref bbaaxx)", notebook) - - # name - Literate.notebook(inputfile, outdir, name = "foobar", execute = false) - notebook = read(joinpath(outdir, "foobar.ipynb"), String) - @test occursin("name: foobar", notebook) - @test !occursin("name: inputfile", notebook) - @test !occursin("name: @__NAME__", notebook) - - # execute = true - Literate.notebook(inputfile, outdir) - expected_outputs = rstrip.(( - """ - "cells": [ - """, + return nb + end + Literate.notebook( + inputfile, outdir, execute = false, + preprocess = x -> replace(replace(x, "PLACEHOLDER1" => "1REDLOHECALP"), "PLACEHOLDER3" => "3REDLOHECALP"), + postprocess = post + ) + notebook = read(joinpath(outdir, "inputfile.ipynb"), String) + @test !occursin("PLACEHOLDER1", notebook) + @test !occursin("PLACEHOLDER2", notebook) + @test !occursin("PLACEHOLDER3", notebook) + @test !occursin("PLACEHOLDER4", notebook) + @test occursin("1REDLOHECALP", notebook) + @test occursin("2REDLOHECALP", notebook) + @test occursin("3REDLOHECALP", notebook) + @test occursin("4REDLOHECALP", notebook) + + # documenter = false (deprecated) + @test_deprecated r"The documenter=false keyword to Literate.notebook is deprecated." begin + Literate.notebook(inputfile, outdir, documenter = false, execute = false) + end + notebook = read(joinpath(outdir, "inputfile.ipynb"), String) + @test !occursin("# [Example](@id example-id", notebook) + @test !occursin("[foo](@ref), [bar](@ref bbaarr)", notebook) + @test !occursin("[baz](@ref), [bax](@ref bbaaxx)", notebook) + + # name + Literate.notebook(inputfile, outdir, name = "foobar", execute = false) + notebook = read(joinpath(outdir, "foobar.ipynb"), String) + @test occursin("name: foobar", notebook) + @test !occursin("name: inputfile", notebook) + @test !occursin("name: @__NAME__", notebook) + + # execute = true + Literate.notebook(inputfile, outdir) + expected_outputs = rstrip.( + [ + """ + "cells": [ + """, + """ + "data": { + "text/plain": "3" + }, + """, + """ + "text": [ + "12345678910" + ] + """, + ] + ) - """ - "data": { - "text/plain": "3" - }, - """, + notebook = read(joinpath(outdir, "inputfile.ipynb"), String) - """ - "text": [ - "12345678910" - ] - """)) - - notebook = read(joinpath(outdir, "inputfile.ipynb"), String) - - lastidx = 1 - for out in expected_outputs - idx = findnext(out, notebook, lastidx) - @test idx !== nothing - lastidx = nextind(notebook, last(idx)) - end + lastidx = 1 + for out in expected_outputs + idx = findnext(out, notebook, lastidx) + @test idx !== nothing + lastidx = nextind(notebook, last(idx)) + end - # issue #31 - write(inputfile, "include(\"issue31.jl\")") - write(joinpath(outdir, "issue31.jl"), "10 + 21") - Literate.notebook(inputfile, outdir) - notebook = read(joinpath(outdir, "inputfile.ipynb"), String) - @test occursin("\"data\": {\n \"text/plain\": \"31\"\n }", notebook) - - # issue #251 (recursive include) - write(inputfile, "include(\"issue251/A.jl\")") - mkdir(joinpath(outdir, "issue251")) - write(joinpath(outdir, "issue251", "A.jl"), "include(\"B.jl\")") - write(joinpath(outdir, "issue251", "B.jl"), "230 + 21") - Literate.notebook(inputfile, outdir) - notebook = read(joinpath(outdir, "inputfile.ipynb"), String) - @test occursin("\"data\": {\n \"text/plain\": \"251\"\n }", notebook) - - # test error when executing notebook - write(inputfile, "for i in 1:10\n println(i)") - r = @test_logs((:error, r"error when executing notebook based on input file: "), match_mode=:any, - try - Literate.notebook(inputfile, outdir) - catch err - err - end) - @test isa(r, ErrorException) - @test occursin("when executing the following code block from inputfile ", r.msg) - @test occursin(inputfile, r.msg) - - # verify that inputfile exists - @test_throws ArgumentError Literate.notebook("nonexistent.jl", outdir) - - # default output directory - @test !isfile("inputfile.ipynb") - Literate.notebook(inputfile; execute=false) - @test isfile("inputfile.ipynb") - - # world time problem with `IJulia.display_dict` - write(inputfile, """ + # issue #31 + write(inputfile, "include(\"issue31.jl\")") + write(joinpath(outdir, "issue31.jl"), "10 + 21") + Literate.notebook(inputfile, outdir) + notebook = read(joinpath(outdir, "inputfile.ipynb"), String) + @test occursin("\"data\": {\n \"text/plain\": \"31\"\n }", notebook) + + # issue #251 (recursive include) + write(inputfile, "include(\"issue251/A.jl\")") + mkdir(joinpath(outdir, "issue251")) + write(joinpath(outdir, "issue251", "A.jl"), "include(\"B.jl\")") + write(joinpath(outdir, "issue251", "B.jl"), "230 + 21") + Literate.notebook(inputfile, outdir) + notebook = read(joinpath(outdir, "inputfile.ipynb"), String) + @test occursin("\"data\": {\n \"text/plain\": \"251\"\n }", notebook) + + # test error when executing notebook + write(inputfile, "for i in 1:10\n println(i)") + r = @test_logs( + (:error, r"error when executing notebook based on input file: "), match_mode = :any, + try + Literate.notebook(inputfile, outdir) + catch err + err + end + ) + @test isa(r, ErrorException) + @test occursin("when executing the following code block from inputfile ", r.msg) + @test occursin(inputfile, r.msg) + + # verify that inputfile exists + @test_throws ArgumentError Literate.notebook("nonexistent.jl", outdir) + + # default output directory + @test !isfile("inputfile.ipynb") + Literate.notebook(inputfile; execute = false) + @test isfile("inputfile.ipynb") + + # world time problem with `IJulia.display_dict` + write( + inputfile, + """ struct VegaLiteRenderable end Base.show(io::IO, ::MIME"application/vnd.vegalite.v2+json", ::VegaLiteRenderable) = write(io, \"\"\" @@ -1348,124 +1376,124 @@ end end \"\"\") Base.Multimedia.istextmime(::MIME{Symbol("application/vnd.vegalite.v2+json")}) = true VegaLiteRenderable() - """) - Literate.notebook(inputfile, outdir) - notebook = read(joinpath(outdir, "inputfile.ipynb"), String) - @test occursin("\"application/vnd.vegalite.v2+json\":", notebook) - - - # Capturing output of more exotic types when executing a notebook - script = """ - using DisplayAs - struct X end - Base.show(io::IO, ::MIME"text/plain", ::X) = print(io, "X as text/plain") - Base.show(io::IO, ::MIME"image/svg+xml", ::X) = print(io, "X as image/svg+xml") - Base.show(io::IO, ::MIME"image/png", ::X) = print(io, "X as image/png") - Base.show(io::IO, ::MIME"image/jpeg", ::X) = print(io, "X as image/jpeg") - Base.show(io::IO, ::MIME"text/markdown", ::X) = print(io, "X as text/markdown") - Base.show(io::IO, ::MIME"text/html", ::X) = print(io, "X as text/html") - Base.show(io::IO, ::MIME"text/latex", ::X) = print(io, "X as text/latex") - Base.show(io::IO, ::MIME"application/x-latex", ::X) = print(io, "X as application/x-latex") - Base.show(io::IO, ::MIME"application/vnd.vegalite.v2+json", ::X) = print(io, "{\\"X\\": \\"as application/vnd.vegalite.v2+json\\"}") - - # DisplayAs does not define the following - Base.show(io::IO, ::MIME"application/x-latex", s::DisplayAs.Showable{>:MIME"application/x-latex"}) = - show(io, MIME"application/x-latex"(), s.content) - Base.show(io::IO, ::MIME"application/vnd.vegalite.v2+json", s::DisplayAs.Showable{>:MIME"application/vnd.vegalite.v2+json"}) = - show(io, MIME"application/vnd.vegalite.v2+json"(), s.content) - xs = [] - for mime in [ - MIME"text/plain", - MIME"image/svg+xml", - MIME"image/png", - MIME"image/jpeg", - MIME"text/markdown", - MIME"text/html", - MIME"text/latex", - MIME"application/x-latex", - MIME"application/vnd.vegalite.v2+json" - ] - x = X() |> DisplayAs.Showable{mime} |> DisplayAs.Text - push!(xs, x) - end - """ - for i in 1:9 - script *= "\n#-\nxs[$i]" - end - write(inputfile, script) - Literate.notebook(inputfile, outdir) - - # Calls to display(x) and display(mime, x) - script = """ - struct DF x end - Base.show(io::IO, ::MIME"text/plain", df::DF) = print(io, "DF(\$(df.x)) as text/plain") - Base.show(io::IO, ::MIME"text/html", df::DF) = print(io, "DF(\$(df.x)) as text/html") - Base.show(io::IO, ::MIME"text/latex", df::DF) = print(io, "DF(\$(df.x)) as text/latex") - #- - foreach(display, [DF(1), DF(2)]) - DF(3) - #- - display(MIME("text/latex"), DF(4)) """ - write(inputfile, script) - Literate.notebook(inputfile, outdir) - json = JSON.parsefile(joinpath(outdir, "inputfile.ipynb")) - cells = json["cells"] - @test length(cells) == 4 - # Cell 2 has 3 outputs: 2 display and one execute result - cellout = cells[2]["outputs"] - @test length(cellout) == 3 - for i in 1:3 - exe_res = i == 3 - @test cellout[i]["output_type"] == (exe_res ? "execute_result" : "display_data") - @test keys(cellout[i]["data"]) == Set(("text/plain", "text/html")) - @test cellout[i]["data"]["text/plain"] == "DF($i) as text/plain" - @test cellout[i]["data"]["text/html"] == Any["DF($i) as text/html"] - @test haskey(cellout[i], "execution_count") == exe_res - end - # Cell 3 has one output from a single display call - cellout = cells[3]["outputs"] - @test length(cellout) == 1 - @test cellout[1]["output_type"] == "display_data" - @test keys(cellout[1]["data"]) == Set(("text/latex",)) - @test cellout[1]["data"]["text/latex"] == "DF(4) as text/latex" - @test !haskey(cellout[1], "execution_count") - - # Softscope - # TODO: Windows CI says here, but no longer: The input file that have been used - # above multiple times without problem now gives - # "SystemError: opening file: Invalid argument" for some reason... - new_inputfile = "inputfile2.jl" - write( - new_inputfile, - """ - ret = 0 - for k = 1:10 - ret += k - end - println("ret = ", ret) - """ - ) - Literate.notebook(new_inputfile, outdir) - @test occursin("ret = 55", read(joinpath(outdir, "inputfile2.ipynb"), String)) - ## Disabled softscope - try - Literate.notebook(new_inputfile, outdir; softscope=false) - error("unreachable") - catch err - @test occursin(r"`?ret`? not defined", sprint(Base.showerror, err)) + ) + Literate.notebook(inputfile, outdir) + notebook = read(joinpath(outdir, "inputfile.ipynb"), String) + @test occursin("\"application/vnd.vegalite.v2+json\":", notebook) + + + # Capturing output of more exotic types when executing a notebook + script = """ + using DisplayAs + struct X end + Base.show(io::IO, ::MIME"text/plain", ::X) = print(io, "X as text/plain") + Base.show(io::IO, ::MIME"image/svg+xml", ::X) = print(io, "X as image/svg+xml") + Base.show(io::IO, ::MIME"image/png", ::X) = print(io, "X as image/png") + Base.show(io::IO, ::MIME"image/jpeg", ::X) = print(io, "X as image/jpeg") + Base.show(io::IO, ::MIME"text/markdown", ::X) = print(io, "X as text/markdown") + Base.show(io::IO, ::MIME"text/html", ::X) = print(io, "X as text/html") + Base.show(io::IO, ::MIME"text/latex", ::X) = print(io, "X as text/latex") + Base.show(io::IO, ::MIME"application/x-latex", ::X) = print(io, "X as application/x-latex") + Base.show(io::IO, ::MIME"application/vnd.vegalite.v2+json", ::X) = print(io, "{\\"X\\": \\"as application/vnd.vegalite.v2+json\\"}") + + # DisplayAs does not define the following + Base.show(io::IO, ::MIME"application/x-latex", s::DisplayAs.Showable{>:MIME"application/x-latex"}) = + show(io, MIME"application/x-latex"(), s.content) + Base.show(io::IO, ::MIME"application/vnd.vegalite.v2+json", s::DisplayAs.Showable{>:MIME"application/vnd.vegalite.v2+json"}) = + show(io, MIME"application/vnd.vegalite.v2+json"(), s.content) + xs = [] + for mime in [ + MIME"text/plain", + MIME"image/svg+xml", + MIME"image/png", + MIME"image/jpeg", + MIME"text/markdown", + MIME"text/html", + MIME"text/latex", + MIME"application/x-latex", + MIME"application/vnd.vegalite.v2+json" + ] + x = X() |> DisplayAs.Showable{mime} |> DisplayAs.Text + push!(xs, x) + end + """ + for i in 1:9 + script *= "\n#-\nxs[$i]" + end + write(inputfile, script) + Literate.notebook(inputfile, outdir) + + # Calls to display(x) and display(mime, x) + script = """ + struct DF x end + Base.show(io::IO, ::MIME"text/plain", df::DF) = print(io, "DF(\$(df.x)) as text/plain") + Base.show(io::IO, ::MIME"text/html", df::DF) = print(io, "DF(\$(df.x)) as text/html") + Base.show(io::IO, ::MIME"text/latex", df::DF) = print(io, "DF(\$(df.x)) as text/latex") + #- + foreach(display, [DF(1), DF(2)]) + DF(3) + #- + display(MIME("text/latex"), DF(4)) + """ + write(inputfile, script) + Literate.notebook(inputfile, outdir) + json = JSON.parsefile(joinpath(outdir, "inputfile.ipynb")) + cells = json["cells"] + @test length(cells) == 4 + # Cell 2 has 3 outputs: 2 display and one execute result + cellout = cells[2]["outputs"] + @test length(cellout) == 3 + for i in 1:3 + exe_res = i == 3 + @test cellout[i]["output_type"] == (exe_res ? "execute_result" : "display_data") + @test keys(cellout[i]["data"]) == Set(("text/plain", "text/html")) + @test cellout[i]["data"]["text/plain"] == "DF($i) as text/plain" + @test cellout[i]["data"]["text/html"] == Any["DF($i) as text/html"] + @test haskey(cellout[i], "execution_count") == exe_res + end + # Cell 3 has one output from a single display call + cellout = cells[3]["outputs"] + @test length(cellout) == 1 + @test cellout[1]["output_type"] == "display_data" + @test keys(cellout[1]["data"]) == Set(("text/latex",)) + @test cellout[1]["data"]["text/latex"] == "DF(4) as text/latex" + @test !haskey(cellout[1], "execution_count") + + # Softscope + # TODO: Windows CI says here, but no longer: The input file that have been used + # above multiple times without problem now gives + # "SystemError: opening file: Invalid argument" for some reason... + new_inputfile = "inputfile2.jl" + write( + new_inputfile, + """ + ret = 0 + for k = 1:10 + ret += k end - end # cd(sandbox) - end # mktempdir -end end + println("ret = ", ret) + """ + ) + Literate.notebook(new_inputfile, outdir) + @test occursin("ret = 55", read(joinpath(outdir, "inputfile2.ipynb"), String)) + ## Disabled softscope + try + Literate.notebook(new_inputfile, outdir; softscope = false) + error("unreachable") + catch err + @test occursin(r"`?ret`? not defined", sprint(Base.showerror, err)) + end + end +end @testset "continue_on_error=true" begin input_with_error = - """ - # The following will print something, then error - print("I always wanted to calculate") - sqrt(-1.0) - """ + """ + # The following will print something, then error + print("I always wanted to calculate") + sqrt(-1.0) + """ mktempdir(@__DIR__) do sandbox inputfile = joinpath(sandbox, "input.jl") write(inputfile, input_with_error) @@ -1529,50 +1557,48 @@ end end end -@testset "Configuration" begin; Base.CoreLogging.with_logger(Base.CoreLogging.NullLogger()) do - mktempdir(@__DIR__) do sandbox - cd(sandbox) do - # write content to inputfile - inputfile = "inputfile.jl" - write(inputfile, content) - outdir = mktempdir(pwd()) - - config=Dict( - "repo_root_url" => "www.example1.com", - "nbviewer_root_url" => "www.example2.com", - "binder_root_url" => "www.example3.com", - ) - - # Overwriting of URLs - withenv("TRAVIS_REPO_SLUG" => "fredrikekre/Literate.jl", - "TRAVIS_TAG" => "", - "TRAVIS_PULL_REQUEST" => "false", - "HAS_JOSH_K_SEAL_OF_APPROVAL" => "true") do - Literate.script(inputfile, outdir; config=config) - end - script = read(joinpath(outdir, "inputfile.jl"), String) - @test occursin("Link to repo root: www.example1.com/file.jl", script) - @test occursin("Link to nbviewer: www.example2.com/file.jl", script) - @test occursin("Link to binder: www.example3.com/file.jl", script) - - # Test pick_codefence function - default_codefence=pick_codefence(Literate.DefaultFlavor(), true, "testname") - @test default_codefence == ("````julia" => "````") - @test default_codefence == pick_codefence(Literate.FranklinFlavor(), true, "testname") - @test default_codefence == pick_codefence(Literate.DocumenterFlavor(), true, "testname") - documenter_codefence = ("````@example testname" => "````") - @test documenter_codefence == pick_codefence(Literate.DocumenterFlavor(), false, "testname") - @test ("```{julia}" => "```") == pick_codefence(Literate.QuartoFlavor(), true, "testname") - @test ("```{julia}" => "```") == pick_codefence(Literate.QuartoFlavor(), false, "testname") - - # Misc default configs - create(; type, kw...) = Literate.create_configuration(inputfile; user_config=Dict(), user_kwargs=kw, type=type) - cfg = create(; type=:md, execute=true) - @test cfg["execute"] - @test cfg["codefence"] == ("````julia" => "````") - cfg = create(; type=:md, execute=false) - @test !cfg["execute"] - @test cfg["codefence"] == ("````@example inputfile" => "````") +@testset "Configuration" begin + with_nullogger_tempdir_cd() do + # write content to inputfile + inputfile = "inputfile.jl" + write(inputfile, content) + outdir = mktempdir(pwd()) + + config = Dict( + "repo_root_url" => "www.example1.com", + "nbviewer_root_url" => "www.example2.com", + "binder_root_url" => "www.example3.com", + ) + + # Overwriting of URLs + withenv( + "TRAVIS_REPO_SLUG" => "fredrikekre/Literate.jl", "TRAVIS_TAG" => "", + "TRAVIS_PULL_REQUEST" => "false", "HAS_JOSH_K_SEAL_OF_APPROVAL" => "true" + ) do + Literate.script(inputfile, outdir; config = config) end + script = read(joinpath(outdir, "inputfile.jl"), String) + @test occursin("Link to repo root: www.example1.com/file.jl", script) + @test occursin("Link to nbviewer: www.example2.com/file.jl", script) + @test occursin("Link to binder: www.example3.com/file.jl", script) + + # Test pick_codefence function + default_codefence = pick_codefence(Literate.DefaultFlavor(), true, "testname") + @test default_codefence == ("````julia" => "````") + @test default_codefence == pick_codefence(Literate.FranklinFlavor(), true, "testname") + @test default_codefence == pick_codefence(Literate.DocumenterFlavor(), true, "testname") + documenter_codefence = ("````@example testname" => "````") + @test documenter_codefence == pick_codefence(Literate.DocumenterFlavor(), false, "testname") + @test ("```{julia}" => "```") == pick_codefence(Literate.QuartoFlavor(), true, "testname") + @test ("```{julia}" => "```") == pick_codefence(Literate.QuartoFlavor(), false, "testname") + + # Misc default configs + create(; type, kw...) = Literate.create_configuration(inputfile; user_config = Dict(), user_kwargs = kw, type = type) + cfg = create(; type = :md, execute = true) + @test cfg["execute"] + @test cfg["codefence"] == ("````julia" => "````") + cfg = create(; type = :md, execute = false) + @test !cfg["execute"] + @test cfg["codefence"] == ("````@example inputfile" => "````") end -end end +end