|
|
|
@ -39,7 +39,7 @@ struct QuartoFlavor <: AbstractFlavor end |
|
|
|
# Parser |
|
|
|
# Parser |
|
|
|
abstract type Chunk end |
|
|
|
abstract type Chunk end |
|
|
|
struct MDChunk <: Chunk |
|
|
|
struct MDChunk <: Chunk |
|
|
|
lines::Vector{Pair{String,String}} # indent and content |
|
|
|
lines::Vector{Pair{String, String}} # indent and content |
|
|
|
end |
|
|
|
end |
|
|
|
MDChunk() = MDChunk(String[]) |
|
|
|
MDChunk() = MDChunk(String[]) |
|
|
|
mutable struct CodeChunk <: Chunk |
|
|
|
mutable struct CodeChunk <: Chunk |
|
|
|
@ -133,29 +133,27 @@ function parse(flavor::AbstractFlavor, content; allow_continued = true) |
|
|
|
return chunks |
|
|
|
return chunks |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
function replace_default(content, sym; |
|
|
|
function replace_default( |
|
|
|
config::Dict, |
|
|
|
content, sym; config::Dict, branch = "gh-pages", commit = "master" |
|
|
|
branch = "gh-pages", |
|
|
|
) |
|
|
|
commit = "master" |
|
|
|
repls = Pair{Any, Any}[] |
|
|
|
) |
|
|
|
|
|
|
|
repls = Pair{Any,Any}[] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# add some shameless advertisement |
|
|
|
# add some shameless advertisement |
|
|
|
if config["credit"]::Bool |
|
|
|
if config["credit"]::Bool |
|
|
|
if sym === :jl |
|
|
|
if sym === :jl |
|
|
|
content *= """ |
|
|
|
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 |
|
|
|
else |
|
|
|
content *= """ |
|
|
|
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 |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
@ -168,7 +166,7 @@ function replace_default(content, sym; |
|
|
|
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 |
|
|
|
@ -229,7 +227,7 @@ function replace_default(content, sym; |
|
|
|
push!(repls, r"\[([^]]+?)\]\(@id .*?\)"s => s"\1") # [foo](@id bar) => foo |
|
|
|
push!(repls, r"\[([^]]+?)\]\(@id .*?\)"s => s"\1") # [foo](@id bar) => foo |
|
|
|
# Convert Documenter admonitions to markdown quotes |
|
|
|
# Convert Documenter admonitions to markdown quotes |
|
|
|
r = r"^# !!! (?<type>\w+)(?: \"(?<title>.+)\")?(?<lines>(\v^# .*$)+)"m |
|
|
|
r = r"^# !!! (?<type>\w+)(?: \"(?<title>.+)\")?(?<lines>(\v^# .*$)+)"m |
|
|
|
adm_to_quote = function(s) |
|
|
|
adm_to_quote = function (s) |
|
|
|
m = match(r, s)::RegexMatch |
|
|
|
m = match(r, s)::RegexMatch |
|
|
|
io = IOBuffer() |
|
|
|
io = IOBuffer() |
|
|
|
print(io, "# > **") |
|
|
|
print(io, "# > **") |
|
|
|
@ -259,11 +257,13 @@ 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 = [(MIME("image/svg+xml"), ".svg"), (MIME("image/png"), ".png"), |
|
|
|
_DEFAULT_IMAGE_FORMATS = [ |
|
|
|
(MIME("image/jpeg"), ".jpeg")] |
|
|
|
(MIME("image/svg+xml"), ".svg"), (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}() |
|
|
|
|
|
|
|
|
|
|
|
# Guess the package (or repository) root url with "master" as fallback |
|
|
|
# Guess the package (or repository) root url with "master" as fallback |
|
|
|
# see JuliaDocs/Documenter.jl#1751 |
|
|
|
# see JuliaDocs/Documenter.jl#1751 |
|
|
|
@ -277,8 +277,8 @@ 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 |
|
|
|
@ -298,8 +298,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, |
|
|
|
) |
|
|
|
) |
|
|
|
@ -332,30 +332,38 @@ function pick_codefence(::QuartoFlavor, execute::Bool, name::AbstractString) |
|
|
|
return "```{julia}" => "```" |
|
|
|
return "```{julia}" => "```" |
|
|
|
end |
|
|
|
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 |
|
|
|
# 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) |
|
|
|
user_config = merge!(user_config, user_kwargs) |
|
|
|
user_config = merge!(user_config, user_kwargs) |
|
|
|
|
|
|
|
|
|
|
|
# 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("The documenter=$(d) keyword to Literate.markdown is deprecated." * |
|
|
|
Base.depwarn( |
|
|
|
" Pass `flavor = Literate.$(d ? "DocumenterFlavor" : "CommonMarkFlavor")()`" * |
|
|
|
"The documenter=$(d) keyword to Literate.markdown is deprecated." * |
|
|
|
" instead.", Symbol("Literate.markdown")) |
|
|
|
" Pass `flavor = Literate.$(d ? "DocumenterFlavor" : "CommonMarkFlavor")()`" * |
|
|
|
|
|
|
|
" instead.", Symbol("Literate.markdown") |
|
|
|
|
|
|
|
) |
|
|
|
user_config["flavor"] = d ? DocumenterFlavor() : CommonMarkFlavor() |
|
|
|
user_config["flavor"] = d ? DocumenterFlavor() : CommonMarkFlavor() |
|
|
|
elseif type === :nb |
|
|
|
elseif type === :nb |
|
|
|
Base.depwarn("The documenter=$(d) keyword to Literate.notebook is deprecated." * |
|
|
|
Base.depwarn( |
|
|
|
" It is not used anymore for notebook output.", Symbol("Literate.notebook")) |
|
|
|
"The documenter=$(d) keyword to Literate.notebook is deprecated." * |
|
|
|
|
|
|
|
" It is not used anymore for notebook output.", |
|
|
|
|
|
|
|
Symbol("Literate.notebook") |
|
|
|
|
|
|
|
) |
|
|
|
elseif type === :jl |
|
|
|
elseif type === :jl |
|
|
|
Base.depwarn("The documenter=$(d) keyword to Literate.script is deprecated." * |
|
|
|
Base.depwarn( |
|
|
|
" It is not used anymore for script output.", Symbol("Literate.script")) |
|
|
|
"The documenter=$(d) keyword to Literate.script is deprecated." * |
|
|
|
|
|
|
|
" It is not used anymore for script output.", |
|
|
|
|
|
|
|
Symbol("Literate.script") |
|
|
|
|
|
|
|
) |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
# Add default config |
|
|
|
# Add default config |
|
|
|
cfg = Dict{String,Any}() |
|
|
|
cfg = Dict{String, Any}() |
|
|
|
cfg["name"] = filename(inputfile) |
|
|
|
cfg["name"] = filename(inputfile) |
|
|
|
cfg["preprocess"] = identity |
|
|
|
cfg["preprocess"] = identity |
|
|
|
cfg["postprocess"] = 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"])" |
|
|
|
cfg["repo_root_url"] = "$(url)/blob/$(cfg["edit_commit"])" |
|
|
|
end |
|
|
|
end |
|
|
|
if (url = get(ENV, "CI_PAGES_URL", nothing)) !== nothing && |
|
|
|
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])" |
|
|
|
cfg["nbviewer_root_url"] = "https://nbviewer.jupyter.org/urls/$(m[1])" |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
@ -479,12 +487,13 @@ 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(inputfile; user_config=user_config, |
|
|
|
config = create_configuration( |
|
|
|
user_kwargs=user_kwargs, type=type) |
|
|
|
inputfile; user_config = user_config, user_kwargs = user_kwargs, type = type |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
# Quarto output does not support execute = true |
|
|
|
# Quarto output does not support execute = true |
|
|
|
if config["flavor"] isa QuartoFlavor && config["execute"] |
|
|
|
if config["flavor"] isa QuartoFlavor && config["execute"] |
|
|
|
@ -505,8 +514,8 @@ function preprocessor(inputfile, outputdir; user_config, user_kwargs, type) |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
output_thing = type === (:md) ? "markdown page" : |
|
|
|
output_thing = 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 |
|
|
|
@ -526,16 +535,17 @@ function preprocessor(inputfile, outputdir; user_config, user_kwargs, type) |
|
|
|
# change the Edit on GitHub link |
|
|
|
# change the Edit on GitHub link |
|
|
|
edit_url = relpath(inputfile, config["literate_outputdir"]) |
|
|
|
edit_url = relpath(inputfile, config["literate_outputdir"]) |
|
|
|
edit_url = replace(edit_url, "\\" => "/") |
|
|
|
edit_url = replace(edit_url, "\\" => "/") |
|
|
|
content = """ |
|
|
|
meta_block = """ |
|
|
|
# ```@meta |
|
|
|
# ```@meta |
|
|
|
# EditURL = "$(edit_url)" |
|
|
|
# EditURL = "$(edit_url)" |
|
|
|
# ``` |
|
|
|
# ``` |
|
|
|
|
|
|
|
|
|
|
|
""" * content |
|
|
|
""" |
|
|
|
|
|
|
|
content = meta_block * content |
|
|
|
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(config["flavor"], content; allow_continued = type !== :nb) |
|
|
|
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 |
|
|
|
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 |
|
|
|
@ -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 |
|
|
|
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 = |
|
|
|
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 |
|
|
|
# create the script file |
|
|
|
ioscript = IOBuffer() |
|
|
|
ioscript = IOBuffer() |
|
|
|
@ -600,10 +610,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 = |
|
|
|
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 |
|
|
|
# create the markdown file |
|
|
|
sb = sandbox() |
|
|
|
sb = sandbox() |
|
|
|
@ -637,15 +647,16 @@ function markdown(inputfile, outputdir=pwd(); config::AbstractDict=Dict(), kwarg |
|
|
|
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!(iomd, sb, join(chunk.lines, '\n'), |
|
|
|
execute_markdown!( |
|
|
|
config["literate_outputdir"]; |
|
|
|
iomd, sb, join(chunk.lines, '\n'), |
|
|
|
inputfile=config["literate_inputfile"], |
|
|
|
config["literate_outputdir"]; |
|
|
|
fake_source=config["literate_outputfile"], |
|
|
|
inputfile = config["literate_inputfile"], |
|
|
|
flavor=config["flavor"], |
|
|
|
fake_source = config["literate_outputfile"], |
|
|
|
image_formats=config["image_formats"], |
|
|
|
flavor = config["flavor"], |
|
|
|
file_prefix="$(config["name"])-$(chunknum)", |
|
|
|
image_formats = config["image_formats"], |
|
|
|
softscope=config["softscope"], |
|
|
|
file_prefix = "$(config["name"])-$(chunknum)", |
|
|
|
continue_on_error=config["continue_on_error"], |
|
|
|
softscope = config["softscope"], |
|
|
|
|
|
|
|
continue_on_error = config["continue_on_error"], |
|
|
|
) |
|
|
|
) |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
@ -661,19 +672,23 @@ function markdown(inputfile, outputdir=pwd(); config::AbstractDict=Dict(), kwarg |
|
|
|
return outputfile |
|
|
|
return outputfile |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
function execute_markdown!(io::IO, sb::Module, block::String, outputdir; |
|
|
|
function execute_markdown!( |
|
|
|
inputfile::String, fake_source::String, |
|
|
|
io::IO, sb::Module, block::String, outputdir; |
|
|
|
flavor::AbstractFlavor, image_formats::Vector, file_prefix::String, |
|
|
|
inputfile::String, fake_source::String, flavor::AbstractFlavor, |
|
|
|
softscope::Bool, continue_on_error::Bool) |
|
|
|
image_formats::Vector, file_prefix::String, softscope::Bool, |
|
|
|
|
|
|
|
continue_on_error::Bool |
|
|
|
|
|
|
|
) |
|
|
|
# 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( |
|
|
|
softscope=softscope, continue_on_error=continue_on_error) |
|
|
|
sb, block; inputfile = inputfile, fake_source = fake_source, |
|
|
|
|
|
|
|
softscope = softscope, continue_on_error = continue_on_error |
|
|
|
|
|
|
|
) |
|
|
|
# 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 = flavor isa FranklinFlavor ? ("~~~" => "~~~") : ("```@raw html" => "```") |
|
|
|
htmlfence = 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) |
|
|
|
@ -731,16 +746,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 = |
|
|
|
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 |
|
|
|
# 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 |
|
|
|
|
|
|
|
|
|
|
|
@ -761,10 +776,12 @@ function jupyter_notebook(chunks, config) |
|
|
|
else |
|
|
|
else |
|
|
|
metadata = Dict{String, Any}() |
|
|
|
metadata = Dict{String, Any}() |
|
|
|
end |
|
|
|
end |
|
|
|
lines = isa(chunk, MDChunk) ? |
|
|
|
if isa(chunk, MDChunk) |
|
|
|
String[x.second for x in chunk.lines] : # skip indent |
|
|
|
lines = String[x.second for x in chunk.lines] # skip indent |
|
|
|
chunk.lines |
|
|
|
else |
|
|
|
@views map!(x -> x * '\n', lines[1:end-1], lines[1:end-1]) |
|
|
|
lines = chunk.lines |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
@views map!(x -> x * '\n', lines[1:(end - 1)], lines[1:(end - 1)]) |
|
|
|
cell["cell_type"] = chunktype |
|
|
|
cell["cell_type"] = chunktype |
|
|
|
cell["metadata"] = metadata |
|
|
|
cell["metadata"] = metadata |
|
|
|
cell["source"] = lines |
|
|
|
cell["source"] = lines |
|
|
|
@ -780,15 +797,15 @@ function jupyter_notebook(chunks, config) |
|
|
|
metadata = Dict() |
|
|
|
metadata = Dict() |
|
|
|
|
|
|
|
|
|
|
|
kernelspec = Dict() |
|
|
|
kernelspec = Dict() |
|
|
|
kernelspec["language"] = "julia" |
|
|
|
kernelspec["language"] = "julia" |
|
|
|
kernelspec["name"] = "julia-$(VERSION.major).$(VERSION.minor)" |
|
|
|
kernelspec["name"] = "julia-$(VERSION.major).$(VERSION.minor)" |
|
|
|
kernelspec["display_name"] = "Julia $(string(VERSION))" |
|
|
|
kernelspec["display_name"] = "Julia $(string(VERSION))" |
|
|
|
metadata["kernelspec"] = kernelspec |
|
|
|
metadata["kernelspec"] = kernelspec |
|
|
|
|
|
|
|
|
|
|
|
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 |
|
|
|
|
|
|
|
|
|
|
|
@ -801,23 +818,26 @@ 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; inputfile=config["literate_inputfile"], |
|
|
|
nb = execute_notebook( |
|
|
|
fake_source=config["literate_outputfile"], |
|
|
|
nb; inputfile = config["literate_inputfile"], |
|
|
|
softscope=config["softscope"], |
|
|
|
fake_source = config["literate_outputfile"], |
|
|
|
continue_on_error=config["continue_on_error"], |
|
|
|
softscope = config["softscope"], |
|
|
|
) |
|
|
|
continue_on_error = config["continue_on_error"], |
|
|
|
|
|
|
|
) |
|
|
|
end |
|
|
|
end |
|
|
|
catch err |
|
|
|
catch err |
|
|
|
@error "error when executing notebook based on input file: " * |
|
|
|
@error "error when executing notebook based on input file: " * |
|
|
|
"`$(Base.contractuser(config["literate_inputfile"]))`" |
|
|
|
"`$(Base.contractuser(config["literate_inputfile"]))`" |
|
|
|
rethrow(err) |
|
|
|
rethrow(err) |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
return nb |
|
|
|
return nb |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
function execute_notebook(nb; inputfile::String, fake_source::String, softscope::Bool, |
|
|
|
function execute_notebook( |
|
|
|
continue_on_error=continue_on_error) |
|
|
|
nb; inputfile::String, fake_source::String, softscope::Bool, |
|
|
|
|
|
|
|
continue_on_error = continue_on_error |
|
|
|
|
|
|
|
) |
|
|
|
sb = sandbox() |
|
|
|
sb = sandbox() |
|
|
|
execution_count = 0 |
|
|
|
execution_count = 0 |
|
|
|
for cell in nb["cells"] |
|
|
|
for cell in nb["cells"] |
|
|
|
@ -825,13 +845,14 @@ function execute_notebook(nb; inputfile::String, fake_source::String, softscope: |
|
|
|
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 = execute_block(sb, block; inputfile=inputfile, |
|
|
|
r, str, display_dicts = execute_block( |
|
|
|
fake_source=fake_source, softscope=softscope, |
|
|
|
sb, block; inputfile = inputfile, fake_source = fake_source, |
|
|
|
continue_on_error=continue_on_error) |
|
|
|
softscope = softscope, continue_on_error = continue_on_error |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
# str should go into stream |
|
|
|
# str should go into stream |
|
|
|
if !isempty(str) |
|
|
|
if !isempty(str) |
|
|
|
stream = Dict{String,Any}() |
|
|
|
stream = Dict{String, Any}() |
|
|
|
stream["output_type"] = "stream" |
|
|
|
stream["output_type"] = "stream" |
|
|
|
stream["name"] = "stdout" |
|
|
|
stream["name"] = "stdout" |
|
|
|
stream["text"] = collect(Any, eachline(IOBuffer(String(str)), keep = true)) |
|
|
|
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(...) |
|
|
|
# Any explicit calls to display(...) |
|
|
|
for dict in display_dicts |
|
|
|
for dict in display_dicts |
|
|
|
display_data = Dict{String,Any}() |
|
|
|
display_data = Dict{String, Any}() |
|
|
|
display_data["output_type"] = "display_data" |
|
|
|
display_data["output_type"] = "display_data" |
|
|
|
display_data["metadata"] = Dict() |
|
|
|
display_data["metadata"] = Dict() |
|
|
|
display_data["data"] = split_mime(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 |
|
|
|
# r should go into execute_result |
|
|
|
if r !== nothing |
|
|
|
if r !== nothing |
|
|
|
execute_result = Dict{String,Any}() |
|
|
|
execute_result = Dict{String, Any}() |
|
|
|
execute_result["output_type"] = "execute_result" |
|
|
|
execute_result["output_type"] = "execute_result" |
|
|
|
execute_result["metadata"] = Dict() |
|
|
|
execute_result["metadata"] = Dict() |
|
|
|
execute_result["execution_count"] = execution_count |
|
|
|
execute_result["execution_count"] = execution_count |
|
|
|
@ -899,7 +920,7 @@ end |
|
|
|
# TODO: Problematic to accept mime::MIME here? |
|
|
|
# TODO: Problematic to accept mime::MIME here? |
|
|
|
function Base.display(ld::LiterateDisplay, mime::MIME, x) |
|
|
|
function Base.display(ld::LiterateDisplay, mime::MIME, x) |
|
|
|
r = Base.invokelatest(IJulia.limitstringmime, 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 |
|
|
|
# TODO: IJulia does this part below for unknown mimes |
|
|
|
# if istextmime(mime) |
|
|
|
# if istextmime(mime) |
|
|
|
# display_dicts["text/plain"] = r |
|
|
|
# display_dicts["text/plain"] = r |
|
|
|
@ -909,8 +930,10 @@ function Base.display(ld::LiterateDisplay, mime::MIME, x) |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
# Execute a code-block in a module and capture stdout/stderr and the result |
|
|
|
# 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, |
|
|
|
function execute_block( |
|
|
|
softscope::Bool, continue_on_error::Bool) |
|
|
|
sb::Module, block::String; inputfile::String, fake_source::String, |
|
|
|
|
|
|
|
softscope::Bool, continue_on_error::Bool |
|
|
|
|
|
|
|
) |
|
|
|
@debug """execute_block($sb, block) |
|
|
|
@debug """execute_block($sb, block) |
|
|
|
``` |
|
|
|
``` |
|
|
|
$(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) |
|
|
|
all_output = c.output * "\n\nERROR: " * sprint(showerror, err) |
|
|
|
return nothing, all_output, disp.data |
|
|
|
return nothing, all_output, disp.data |
|
|
|
else |
|
|
|
else |
|
|
|
error(""" |
|
|
|
error( |
|
|
|
$(sprint(showerror, c.value)) |
|
|
|
""" |
|
|
|
when executing the following code block from inputfile `$(Base.contractuser(inputfile))` |
|
|
|
$(sprint(showerror, c.value)) |
|
|
|
|
|
|
|
when executing the following code block from inputfile `$(Base.contractuser(inputfile))` |
|
|
|
```julia |
|
|
|
|
|
|
|
$block |
|
|
|
```julia |
|
|
|
``` |
|
|
|
$block |
|
|
|
""") |
|
|
|
``` |
|
|
|
|
|
|
|
""" |
|
|
|
|
|
|
|
) |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
return c.value, c.output, disp.data |
|
|
|
return c.value, c.output, disp.data |
|
|
|
|