Browse Source

Revert "Formatted"

This reverts commit fa050c7863.
pull/200/head
J S 3 years ago
parent
commit
76183c0476
  1. 23
      docs/make.jl
  2. 4
      docs/src/outputformats.jl
  3. 2
      examples/README.jl
  4. 6
      examples/example.jl
  5. 7
      src/IJulia.jl
  6. 225
      src/Literate.jl
  7. 623
      test/runtests.jl

23
docs/make.jl

@ -5,11 +5,7 @@ if haskey(ENV, "GITHUB_ACTIONS")
end end
deployconfig = Documenter.auto_detect_deploy_system() deployconfig = Documenter.auto_detect_deploy_system()
Documenter.post_status( Documenter.post_status(deployconfig; type="pending", repo="github.com/fredrikekre/Literate.jl.git")
deployconfig;
type = "pending",
repo = "github.com/fredrikekre/Literate.jl.git",
)
using Literate using Literate
using Plots # to not capture precompilation output using Plots # to not capture precompilation output
@ -18,7 +14,7 @@ EXAMPLE = joinpath(@__DIR__, "..", "examples", "example.jl")
OUTPUT = joinpath(@__DIR__, "src/generated") OUTPUT = joinpath(@__DIR__, "src/generated")
function preprocess(str) function preprocess(str)
str = replace(str, "x = 123" => "y = 321"; count = 1) str = replace(str, "x = 123" => "y = 321"; count=1)
return str return str
end end
@ -27,12 +23,7 @@ Literate.notebook(EXAMPLE, OUTPUT, preprocess = preprocess)
Literate.script(EXAMPLE, OUTPUT, preprocess = preprocess) Literate.script(EXAMPLE, OUTPUT, preprocess = preprocess)
# generate the example notebook for the documentation, keep in sync with outputformats.md # generate the example notebook for the documentation, keep in sync with outputformats.md
Literate.markdown( Literate.markdown(joinpath(@__DIR__, "src/outputformats.jl"), OUTPUT; credit = false, name = "name")
joinpath(@__DIR__, "src/outputformats.jl"),
OUTPUT;
credit = false,
name = "name",
)
Literate.notebook(joinpath(@__DIR__, "src/outputformats.jl"), OUTPUT; name = "notebook") Literate.notebook(joinpath(@__DIR__, "src/outputformats.jl"), OUTPUT; name = "notebook")
Literate.script(joinpath(@__DIR__, "src/outputformats.jl"), OUTPUT; credit = false) Literate.script(joinpath(@__DIR__, "src/outputformats.jl"), OUTPUT; credit = false)
@ -50,10 +41,7 @@ if haskey(ENV, "GITHUB_ACTIONS")
end end
url = "https://nbviewer.jupyter.org/github/fredrikekre/Literate.jl/blob/gh-pages/$(folder)/" url = "https://nbviewer.jupyter.org/github/fredrikekre/Literate.jl/blob/gh-pages/$(folder)/"
str = read(joinpath(@__DIR__, "src/outputformats.md"), String) str = read(joinpath(@__DIR__, "src/outputformats.md"), String)
str = replace( str = replace(str, "[notebook.ipynb](generated/notebook.ipynb)." => "[notebook.ipynb]($(url)generated/notebook.ipynb).")
str,
"[notebook.ipynb](generated/notebook.ipynb)." => "[notebook.ipynb]($(url)generated/notebook.ipynb).",
)
write(joinpath(@__DIR__, "src/outputformats.md"), str) write(joinpath(@__DIR__, "src/outputformats.md"), str)
end end
@ -73,8 +61,7 @@ makedocs(
"customprocessing.md", "customprocessing.md",
"documenter.md", "documenter.md",
"tips.md", "tips.md",
"generated/example.md", "generated/example.md"]
],
) )
deploydocs( deploydocs(

4
docs/src/outputformats.jl

@ -3,9 +3,9 @@
# In julia rational numbers can be constructed with the `//` operator. # In julia rational numbers can be constructed with the `//` operator.
# Lets define two rational numbers, `x` and `y`: # 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: # When adding `x` and `y` together we obtain a new rational number:

2
examples/README.jl

@ -20,7 +20,7 @@
# running these commands from the package root of Literate.jl: # running these commands from the package root of Literate.jl:
using Literate using Literate
Literate.markdown("examples/README.jl", "."; flavor = Literate.CommonMarkFlavor()) Literate.markdown("examples/README.jl", "."; flavor=Literate.CommonMarkFlavor())
# [docs-img]: https://img.shields.io/badge/docs-latest%20release-blue.svg # [docs-img]: https://img.shields.io/badge/docs-latest%20release-blue.svg
# [docs-url]: https://fredrikekre.github.io/Literate.jl/ # [docs-url]: https://fredrikekre.github.io/Literate.jl/

6
examples/example.jl

@ -33,8 +33,8 @@
# as markdown, and all the other lines are interpreted as code. Here is some code: # as markdown, and all the other lines are interpreted as code. Here is some code:
#nb %% A slide [code] {"slideshow": {"slide_type": "fragment"}} #nb %% A slide [code] {"slideshow": {"slide_type": "fragment"}}
x = 1 // 3 x = 1//3
y = 2 // 5 y = 2//5
#nb # %% A slide [markdown] {"slideshow": {"slide_type": "subslide"}} #nb # %% A slide [markdown] {"slideshow": {"slide_type": "subslide"}}
# In markdown sections we can use markdown syntax. For example, we can # 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"}} #nb %% A slide [code] {"slideshow": {"slide_type": "subslide"}}
using Plots using Plots
x = range(0, stop = 6π, length = 1000) x = range(0, stop=6π, length=1000)
y1 = sin.(x) y1 = sin.(x)
y2 = cos.(x) y2 = cos.(x)
plot(x, [y1, y2]) plot(x, [y1, y2])

7
src/IJulia.jl

@ -19,8 +19,7 @@ const application_vnd_vegalite_v2 = MIME("application/vnd.vegalite.v2+json")
function display_dict(x) 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) if showable(application_vnd_vegalite_v2, x)
data[string(application_vnd_vegalite_v2)] = data[string(application_vnd_vegalite_v2)] = JSON.parse(limitstringmime(application_vnd_vegalite_v2, x))
JSON.parse(limitstringmime(application_vnd_vegalite_v2, x))
end end
if showable(image_svg, x) if showable(image_svg, x)
data[string(image_svg)] = limitstringmime(image_svg, x) data[string(image_svg)] = limitstringmime(image_svg, x)
@ -58,14 +57,14 @@ function limitstringmime(mime::MIME, x)
if israwtext(mime, x) if israwtext(mime, x)
return String(x) return String(x)
else else
show(IOContext(buf, :limit => true, :color => true), mime, x) show(IOContext(buf, :limit=>true, :color=>true), mime, x)
end end
else else
b64 = Base64EncodePipe(buf) b64 = Base64EncodePipe(buf)
if isa(x, Vector{UInt8}) if isa(x, Vector{UInt8})
write(b64, x) # x assumed to be raw binary data write(b64, x) # x assumed to be raw binary data
else else
show(IOContext(b64, :limit => true, :color => true), mime, x) show(IOContext(b64, :limit=>true, :color=>true), mime, x)
end end
close(b64) close(b64)
end end

225
src/Literate.jl

@ -44,9 +44,7 @@ mutable struct CodeChunk <: Chunk
end end
CodeChunk() = CodeChunk(String[], false) CodeChunk() = CodeChunk(String[], false)
ismdline(line) = ismdline(line) = (occursin(r"^\h*#$", line) || occursin(r"^\h*# .*$", line)) && !occursin(r"^\h*##", line)
(occursin(r"^\h*#$", line) || occursin(r"^\h*# .*$", line)) &&
!occursin(r"^\h*##", line)
function parse(content; allow_continued = true) function parse(content; allow_continued = true)
lines = collect(eachline(IOBuffer(content))) lines = collect(eachline(IOBuffer(content)))
@ -80,10 +78,7 @@ function parse(content; allow_continued = true)
end end
# remove "## " and "##\n", strip the leading "#" from "## xyz" and "##| xyz" # remove "## " and "##\n", strip the leading "#" from "## xyz" and "##| xyz"
# Note: accepts only standard space character (not no-break space U+00A0) # Note: accepts only standard space character (not no-break space U+00A0)
line = replace( line = replace(replace(line, r"^(\h*)#(#(:? |\|).*)$" => s"\1\2"), r"^(\h*#)#$" => s"\1")
replace(line, r"^(\h*)#(#(:? |\|).*)$" => s"\1\2"),
r"^(\h*#)#$" => s"\1",
)
push!(chunks[end].lines, line) push!(chunks[end].lines, line)
end end
end end
@ -113,10 +108,7 @@ function parse(content; allow_continued = true)
append!(merged_chunks[end].lines, chunk.lines) append!(merged_chunks[end].lines, chunk.lines)
else # need to put back "#" else # need to put back "#"
for line in chunk.lines for line in chunk.lines
push!( push!(merged_chunks[end].lines, rstrip(line.first * "# " * line.second))
merged_chunks[end].lines,
rstrip(line.first * "# " * line.second),
)
end end
end end
else else
@ -132,7 +124,11 @@ function parse(content; allow_continued = true)
return chunks return chunks
end end
function replace_default(content, sym; config::Dict, branch = "gh-pages", commit = "master") function replace_default(content, sym;
config::Dict,
branch = "gh-pages",
commit = "master"
)
repls = Pair{Any,Any}[] repls = Pair{Any,Any}[]
# add some shameless advertisement # add some shameless advertisement
@ -163,7 +159,7 @@ function replace_default(content, sym; config::Dict, branch = "gh-pages", commit
newlines = sprint() do io newlines = sprint() do io
foreach(l -> println(io, "# ", l), eachline(IOBuffer(m[1]))) foreach(l -> println(io, "# ", l), eachline(IOBuffer(m[1])))
end end
str = replace(str, multiline_r => chop(newlines); count = 1) str = replace(str, multiline_r => chop(newlines); count=1)
end end
return str return str
end end
@ -203,16 +199,12 @@ function replace_default(content, sym; config::Dict, branch = "gh-pages", commit
# fix links # fix links
if get(ENV, "DOCUMENTATIONGENERATOR", "") == "true" if get(ENV, "DOCUMENTATIONGENERATOR", "") == "true"
## DocumentationGenerator.jl ## DocumentationGenerator.jl
base_url = base_url = get(ENV, "DOCUMENTATIONGENERATOR_BASE_URL", "DOCUMENTATIONGENERATOR_BASE_URL")
get(ENV, "DOCUMENTATIONGENERATOR_BASE_URL", "DOCUMENTATIONGENERATOR_BASE_URL")
nbviewer_root_url = "https://nbviewer.jupyter.org/urls/$(base_url)" nbviewer_root_url = "https://nbviewer.jupyter.org/urls/$(base_url)"
push!(repls, "@__NBVIEWER_ROOT_URL__" => nbviewer_root_url) push!(repls, "@__NBVIEWER_ROOT_URL__" => nbviewer_root_url)
else else
push!(repls, "@__REPO_ROOT_URL__" => get(config, "repo_root_url", "<unknown>")) push!(repls, "@__REPO_ROOT_URL__" => get(config, "repo_root_url", "<unknown>"))
push!( push!(repls, "@__NBVIEWER_ROOT_URL__" => get(config, "nbviewer_root_url", "<unknown>"))
repls,
"@__NBVIEWER_ROOT_URL__" => get(config, "nbviewer_root_url", "<unknown>"),
)
push!(repls, "@__BINDER_ROOT_URL__" => get(config, "binder_root_url", "<unknown>")) push!(repls, "@__BINDER_ROOT_URL__" => get(config, "binder_root_url", "<unknown>"))
end end
@ -235,11 +227,8 @@ end
filename(str) = first(splitext(last(splitdir(str)))) filename(str) = first(splitext(last(splitdir(str))))
isdocumenter(cfg) = cfg["flavor"]::AbstractFlavor isa DocumenterFlavor isdocumenter(cfg) = cfg["flavor"]::AbstractFlavor isa DocumenterFlavor
_DEFAULT_IMAGE_FORMATS = [ _DEFAULT_IMAGE_FORMATS = [(MIME("image/svg+xml"), ".svg"), (MIME("image/png"), ".png"),
(MIME("image/svg+xml"), ".svg"), (MIME("image/jpeg"), ".jpeg")]
(MIME("image/png"), ".png"),
(MIME("image/jpeg"), ".jpeg"),
]
# Cache of inputfile => head branch # Cache of inputfile => head branch
const HEAD_BRANCH_CACHE = Dict{String,String}() const HEAD_BRANCH_CACHE = Dict{String,String}()
@ -256,9 +245,9 @@ function edit_commit(inputfile, user_config)
git_root = try git_root = try
readchomp( readchomp(
pipeline( pipeline(
setenv(`$(git) rev-parse --show-toplevel`; dir = dirname(inputfile)); setenv(`$(git) rev-parse --show-toplevel`; dir=dirname(inputfile));
stderr = devnull, stderr=devnull,
), )
) )
catch catch
end end
@ -277,8 +266,8 @@ function edit_commit(inputfile, user_config)
str = try str = try
read( read(
pipeline( pipeline(
setenv(`$(git) remote show origin`, env; dir = dirname(inputfile)), setenv(`$(git) remote show origin`, env; dir=dirname(inputfile)),
stderr = devnull, stderr=devnull,
), ),
String, String,
) )
@ -294,20 +283,16 @@ function edit_commit(inputfile, user_config)
end end
# Default to DefaultFlavor() setting # Default to DefaultFlavor() setting
pick_codefence(flavor::AbstractFlavor, execute::Bool, name::AbstractString) = pick_codefence(flavor::AbstractFlavor,execute::Bool,name::AbstractString)=pick_codefence(DefaultFlavor(),execute,name)
pick_codefence(DefaultFlavor(), execute, name) pick_codefence(flavor::DefaultFlavor,execute::Bool,name::AbstractString)=("````julia" => "````")
pick_codefence(flavor::DefaultFlavor, execute::Bool, name::AbstractString) = pick_codefence(flavor::DocumenterFlavor,execute::Bool,name::AbstractString)=(execute ?
("````julia" => "````") pick_codefence(DefaultFlavor(),execute,name) : ("````@example $(name)" => "````")
pick_codefence(flavor::DocumenterFlavor, execute::Bool, name::AbstractString) = (
execute ? pick_codefence(DefaultFlavor(), execute, name) :
("````@example $(name)" => "````")
) )
pick_codefence(flavor::QuartoFlavor, execute::Bool, name::AbstractString) = ( pick_codefence(flavor::QuartoFlavor,execute::Bool,name::AbstractString)=(execute ?
execute ? error("QuartoFlavor does not support argument execute=true!") : error("QuartoFlavor does not support argument execute=true!") : ("```{julia}" => "```")
("```{julia}" => "```")
) )
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 # Combine user config with user kwargs
user_config = Dict{String,Any}(string(k) => v for (k, v) in user_config) 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_kwargs = Dict{String,Any}(string(k) => v for (k, v) in user_kwargs)
@ -316,25 +301,16 @@ function create_configuration(inputfile; user_config, user_kwargs, type = nothin
# deprecation of documenter kwarg # deprecation of documenter kwarg
if (d = get(user_config, "documenter", nothing); d !== nothing) if (d = get(user_config, "documenter", nothing); d !== nothing)
if type === :md if type === :md
Base.depwarn( Base.depwarn("The documenter=$(d) keyword to Literate.markdown is deprecated." *
"The documenter=$(d) keyword to Literate.markdown is deprecated." *
" Pass `flavor = Literate.$(d ? "DocumenterFlavor" : "CommonMarkFlavor")()`" * " Pass `flavor = Literate.$(d ? "DocumenterFlavor" : "CommonMarkFlavor")()`" *
" instead.", " instead.", Symbol("Literate.markdown"))
Symbol("Literate.markdown"),
)
user_config["flavor"] = d ? DocumenterFlavor() : CommonMarkFlavor() user_config["flavor"] = d ? DocumenterFlavor() : CommonMarkFlavor()
elseif type === :nb elseif type === :nb
Base.depwarn( Base.depwarn("The documenter=$(d) keyword to Literate.notebook is deprecated." *
"The documenter=$(d) keyword to Literate.notebook is deprecated." * " It is not used anymore for notebook output.", Symbol("Literate.notebook"))
" It is not used anymore for notebook output.",
Symbol("Literate.notebook"),
)
elseif type === :jl elseif type === :jl
Base.depwarn( Base.depwarn("The documenter=$(d) keyword to Literate.script is deprecated." *
"The documenter=$(d) keyword to Literate.script is deprecated." * " It is not used anymore for script output.", Symbol("Literate.script"))
" It is not used anymore for script output.",
Symbol("Literate.script"),
)
end end
end end
@ -351,7 +327,7 @@ function create_configuration(inputfile; user_config, user_kwargs, type = nothin
cfg["codefence"] = pick_codefence( cfg["codefence"] = pick_codefence(
get(user_config, "flavor", cfg["flavor"]), get(user_config, "flavor", cfg["flavor"]),
get(user_config, "execute", cfg["execute"]), get(user_config, "execute", cfg["execute"]),
get(user_config, "name", replace(cfg["name"], r"\s" => "_")), get(user_config, "name", replace(cfg["name"], r"\s" => "_"))
) )
cfg["image_formats"] = _DEFAULT_IMAGE_FORMATS cfg["image_formats"] = _DEFAULT_IMAGE_FORMATS
cfg["edit_commit"] = edit_commit(inputfile, user_config) cfg["edit_commit"] = edit_commit(inputfile, user_config)
@ -391,8 +367,7 @@ function create_configuration(inputfile; user_config, user_kwargs, type = nothin
else else
get(user_config, "devurl", "dev") get(user_config, "devurl", "dev")
end end
elseif (m = match(r"refs\/pull\/(\d+)\/merge", get(ENV, "GITHUB_REF", ""))) !== elseif (m = match(r"refs\/pull\/(\d+)\/merge", get(ENV, "GITHUB_REF", ""))) !== nothing
nothing
"previews/PR$(m.captures[1])" "previews/PR$(m.captures[1])"
else else
"dev" "dev"
@ -468,16 +443,12 @@ Available options:
`$(_DEFAULT_IMAGE_FORMATS)`. Results which are `showable` with a MIME type are saved with `$(_DEFAULT_IMAGE_FORMATS)`. Results which are `showable` with a MIME type are saved with
the first match, with the corresponding extension. 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) function preprocessor(inputfile, outputdir; user_config, user_kwargs, type)
# Create configuration by merging default and userdefined # Create configuration by merging default and userdefined
config = create_configuration( config = create_configuration(inputfile; user_config=user_config,
inputfile; user_kwargs=user_kwargs, type=type)
user_config = user_config,
user_kwargs = user_kwargs,
type = type,
)
# normalize paths # normalize paths
inputfile = normpath(inputfile) inputfile = normpath(inputfile)
@ -486,21 +457,15 @@ function preprocessor(inputfile, outputdir; user_config, user_kwargs, type)
mkpath(outputdir) mkpath(outputdir)
outputdir = realpath(abspath(outputdir)) outputdir = realpath(abspath(outputdir))
isdir(outputdir) || error("not a directory: $(outputdir)") isdir(outputdir) || error("not a directory: $(outputdir)")
ext = ext = type === (:nb) ? ".ipynb" : (type === (:md) && config["flavor"] isa QuartoFlavor) ? ".qmd" : ".$(type)"
type === (:nb) ? ".ipynb" :
(type === (:md) && config["flavor"] isa QuartoFlavor) ? ".qmd" : ".$(type)"
outputfile = joinpath(outputdir, config["name"]::String * ext) outputfile = joinpath(outputdir, config["name"]::String * ext)
if inputfile == outputfile if inputfile == outputfile
throw( throw(ArgumentError("outputfile (`$outputfile`) is identical to inputfile (`$inputfile`)"))
ArgumentError(
"outputfile (`$outputfile`) is identical to inputfile (`$inputfile`)",
),
)
end end
output_thing = output_thing = type === (:md) ? "markdown page" :
type === (:md) ? "markdown page" : type === (:nb) ? "notebook" :
type === (:nb) ? "notebook" : type === (:jl) ? "plain script file" : error("nope") type === (:jl) ? "plain script file" : error("nope")
@info "generating $(output_thing) from `$(Base.contractuser(inputfile))`" @info "generating $(output_thing) from `$(Base.contractuser(inputfile))`"
# Add some information for passing around Literate methods # Add some information for passing around Literate methods
@ -529,7 +494,7 @@ function preprocessor(inputfile, outputdir; user_config, user_kwargs, type)
end end
# default replacements # default replacements
content = replace_default(content, type; config = config) content = replace_default(content, type; config=config)
# parse the content into chunks # parse the content into chunks
chunks = parse(content; allow_continued = type !== :nb) chunks = parse(content; allow_continued = type !== :nb)
@ -537,7 +502,7 @@ function preprocessor(inputfile, outputdir; user_config, user_kwargs, type)
return chunks, config return chunks, config
end end
function write_result(content, config; print = print) function write_result(content, config; print=print)
outputfile = config["literate_outputfile"] outputfile = config["literate_outputfile"]
@info "writing result to `$(Base.contractuser(outputfile))`" @info "writing result to `$(Base.contractuser(outputfile))`"
open(outputfile, "w") do io open(outputfile, "w") do io
@ -554,15 +519,10 @@ Generate a plain script file from `inputfile` and write the result to `outputdir
See the manual section on [Configuration](@ref) for documentation See the manual section on [Configuration](@ref) for documentation
of possible configuration with `config` and other keyword arguments. 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 # preprocessing and parsing
chunks, config = preprocessor( chunks, config =
inputfile, preprocessor(inputfile, outputdir; user_config=config, user_kwargs=kwargs, type=:jl)
outputdir;
user_config = config,
user_kwargs = kwargs,
type = :jl,
)
# create the script file # create the script file
ioscript = IOBuffer() ioscript = IOBuffer()
@ -598,15 +558,10 @@ to the directory `outputdir`.
See the manual section on [Configuration](@ref) for documentation See the manual section on [Configuration](@ref) for documentation
of possible configuration with `config` and other keyword arguments. 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 # preprocessing and parsing
chunks, config = preprocessor( chunks, config =
inputfile, preprocessor(inputfile, outputdir; user_config=config, user_kwargs=kwargs, type=:md)
outputdir;
user_config = config,
user_kwargs = kwargs,
type = :md,
)
# create the markdown file # create the markdown file
sb = sandbox() sb = sandbox()
@ -623,9 +578,7 @@ function markdown(inputfile, outputdir = pwd(); config::AbstractDict = Dict(), k
write(iocode, codefence.first) write(iocode, codefence.first)
# make sure the code block is finalized if we are printing to ```@example # make sure the code block is finalized if we are printing to ```@example
# (or ````@example, any number of backticks >= 3 works) # (or ````@example, any number of backticks >= 3 works)
if chunk.continued && if chunk.continued && occursin(r"^`{3,}@example", codefence.first) && isdocumenter(config)
occursin(r"^`{3,}@example", codefence.first) &&
isdocumenter(config)
write(iocode, "; continued = true") write(iocode, "; continued = true")
end end
write(iocode, '\n') write(iocode, '\n')
@ -643,16 +596,11 @@ function markdown(inputfile, outputdir = pwd(); config::AbstractDict = Dict(), k
any(write_line, chunk.lines) && write(iomd, seekstart(iocode)) any(write_line, chunk.lines) && write(iomd, seekstart(iocode))
if execute if execute
cd(config["literate_outputdir"]) do cd(config["literate_outputdir"]) do
execute_markdown!( execute_markdown!(iomd, sb, join(chunk.lines, '\n'), outputdir;
iomd, inputfile=config["literate_inputfile"],
sb, fake_source=config["literate_outputfile"],
join(chunk.lines, '\n'), flavor=config["flavor"],
outputdir; image_formats=config["image_formats"])
inputfile = config["literate_inputfile"],
fake_source = config["literate_outputfile"],
flavor = config["flavor"],
image_formats = config["image_formats"],
)
end end
end end
end end
@ -667,26 +615,18 @@ function markdown(inputfile, outputdir = pwd(); config::AbstractDict = Dict(), k
return outputfile return outputfile
end end
function execute_markdown!( function execute_markdown!(io::IO, sb::Module, block::String, outputdir;
io::IO, inputfile::String, fake_source::String,
sb::Module, flavor::AbstractFlavor, image_formats::Vector)
block::String,
outputdir;
inputfile::String,
fake_source::String,
flavor::AbstractFlavor,
image_formats::Vector,
)
# TODO: Deal with explicit display(...) calls # TODO: Deal with explicit display(...) calls
r, str, _ = execute_block(sb, block; inputfile = inputfile, fake_source = fake_source) r, str, _ = execute_block(sb, block; inputfile=inputfile, fake_source=fake_source)
# issue #101: consecutive codefenced blocks need newline # issue #101: consecutive codefenced blocks need newline
# issue #144: quadruple backticks allow for triple backticks in the output # 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 r !== nothing && !REPL.ends_with_semicolon(block)
if (flavor isa FranklinFlavor || flavor isa DocumenterFlavor) && if (flavor isa FranklinFlavor || flavor isa DocumenterFlavor) &&
Base.invokelatest(showable, MIME("text/html"), r) Base.invokelatest(showable, MIME("text/html"), r)
htmlfence = htmlfence = flavor isa FranklinFlavor ? ("~~~" => "~~~") : ("```@raw html" => "```")
flavor isa FranklinFlavor ? ("~~~" => "~~~") : ("```@raw html" => "```")
write(io, "\n", htmlfence.first, "\n") write(io, "\n", htmlfence.first, "\n")
Base.invokelatest(show, io, MIME("text/html"), r) Base.invokelatest(show, io, MIME("text/html"), r)
write(io, "\n", htmlfence.second, "\n") write(io, "\n", htmlfence.second, "\n")
@ -728,8 +668,8 @@ function parse_nbmeta(line)
# Cf. https://jupytext.readthedocs.io/en/latest/formats.html#the-percent-format # Cf. https://jupytext.readthedocs.io/en/latest/formats.html#the-percent-format
m = match(r"^%% ([^[{]+)?\s*(?:\[(\w+)\])?\s*(\{.*)?$", line) m = match(r"^%% ([^[{]+)?\s*(?:\[(\w+)\])?\s*(\{.*)?$", line)
typ = m.captures[2] typ = m.captures[2]
name = m.captures[1] === nothing ? Dict{String,String}() : Dict("name" => m.captures[1]) name = m.captures[1] === nothing ? Dict{String, String}() : Dict("name" => m.captures[1])
meta = m.captures[3] === nothing ? Dict{String,Any}() : JSON.parse(m.captures[3]) meta = m.captures[3] === nothing ? Dict{String, Any}() : JSON.parse(m.captures[3])
return typ, merge(name, meta) return typ, merge(name, meta)
end end
line_is_nbmeta(line::Pair) = line_is_nbmeta(line.second) line_is_nbmeta(line::Pair) = line_is_nbmeta(line.second)
@ -743,21 +683,16 @@ Generate a notebook from `inputfile` and write the result to `outputdir`.
See the manual section on [Configuration](@ref) for documentation See the manual section on [Configuration](@ref) for documentation
of possible configuration with `config` and other keyword arguments. 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 # preprocessing and parsing
chunks, config = preprocessor( chunks, config =
inputfile, preprocessor(inputfile, outputdir; user_config=config, user_kwargs=kwargs, type=:nb)
outputdir;
user_config = config,
user_kwargs = kwargs,
type = :nb,
)
# create the notebook # create the notebook
nb = jupyter_notebook(chunks, config) nb = jupyter_notebook(chunks, config)
# write to file # 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 return outputfile
end end
@ -773,15 +708,13 @@ function jupyter_notebook(chunks, config)
chunktype = isa(chunk, MDChunk) ? "markdown" : "code" chunktype = isa(chunk, MDChunk) ? "markdown" : "code"
if !isempty(chunk.lines) && line_is_nbmeta(chunk.lines[1]) if !isempty(chunk.lines) && line_is_nbmeta(chunk.lines[1])
metatype, metadata = parse_nbmeta(chunk.lines[1]) metatype, metadata = parse_nbmeta(chunk.lines[1])
metatype !== nothing && metatype !== nothing && metatype != chunktype && error("specifying a different cell type is not supported")
metatype != chunktype &&
error("specifying a different cell type is not supported")
popfirst!(chunk.lines) popfirst!(chunk.lines)
else else
metadata = Dict{String,Any}() metadata = Dict{String, Any}()
end end
lines = lines = isa(chunk, MDChunk) ?
isa(chunk, MDChunk) ? String[x.second for x in chunk.lines] : # skip indent String[x.second for x in chunk.lines] : # skip indent
chunk.lines chunk.lines
@views map!(x -> x * '\n', lines[1:end-1], lines[1:end-1]) @views map!(x -> x * '\n', lines[1:end-1], lines[1:end-1])
cell["cell_type"] = chunktype cell["cell_type"] = chunktype
@ -807,7 +740,7 @@ function jupyter_notebook(chunks, config)
language_info = Dict() language_info = Dict()
language_info["file_extension"] = ".jl" language_info["file_extension"] = ".jl"
language_info["mimetype"] = "application/julia" language_info["mimetype"] = "application/julia"
language_info["name"] = "julia" language_info["name"]= "julia"
language_info["version"] = string(VERSION) language_info["version"] = string(VERSION)
metadata["language_info"] = language_info metadata["language_info"] = language_info
@ -820,11 +753,8 @@ function jupyter_notebook(chunks, config)
@info "executing notebook `$(config["name"] * ".ipynb")`" @info "executing notebook `$(config["name"] * ".ipynb")`"
try try
cd(config["literate_outputdir"]) do cd(config["literate_outputdir"]) do
nb = execute_notebook( nb = execute_notebook(nb; inputfile=config["literate_inputfile"],
nb; fake_source=config["literate_outputfile"])
inputfile = config["literate_inputfile"],
fake_source = config["literate_outputfile"],
)
end end
catch err catch err
@error "error when executing notebook based on input file: " * @error "error when executing notebook based on input file: " *
@ -843,8 +773,7 @@ function execute_notebook(nb; inputfile::String, fake_source::String)
execution_count += 1 execution_count += 1
cell["execution_count"] = execution_count cell["execution_count"] = execution_count
block = join(cell["source"]) block = join(cell["source"])
r, str, display_dicts = r, str, display_dicts = execute_block(sb, block; inputfile=inputfile, fake_source=fake_source)
execute_block(sb, block; inputfile = inputfile, fake_source = fake_source)
# str should go into stream # str should go into stream
if !isempty(str) if !isempty(str)
@ -946,16 +875,14 @@ function execute_block(sb::Module, block::String; inputfile::String, fake_source
end end
popdisplay(disp) # IOCapture.capture has a try-catch so should always end up here popdisplay(disp) # IOCapture.capture has a try-catch so should always end up here
if c.error if c.error
error( error("""
"""
$(sprint(showerror, c.value)) $(sprint(showerror, c.value))
when executing the following code block from inputfile `$(Base.contractuser(inputfile))` when executing the following code block from inputfile `$(Base.contractuser(inputfile))`
```julia ```julia
$block $block
``` ```
""", """)
)
end end
return c.value, c.output, disp.data return c.value, c.output, disp.data
end end

623
test/runtests.jl

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save