diff --git a/src/converter/markdown/blocks.jl b/src/converter/markdown/blocks.jl
index 21cb0eb31..f2aadab8e 100644
--- a/src/converter/markdown/blocks.jl
+++ b/src/converter/markdown/blocks.jl
@@ -15,6 +15,7 @@ function convert_block(β::AbstractBlock, lxdefs::Vector{LxDef})::AS
βn == :CODE_INLINE && return html_code_inline(stent(β) |> htmlesc)
βn == :CODE_BLOCK_LANG && return resolve_code_block(β.ss)
βn == :CODE_BLOCK! && return resolve_code_block(β.ss, shortcut=true)
+ βn == :CODE_REPL && return resolve_code_block(β.ss, repl=true)
βn == :CODE_BLOCK && return html_code(stent(β), "{{fill lang}}")
βn == :CODE_BLOCK_IND && return convert_indented_code_block(β.ss)
diff --git a/src/eval/codeblock.jl b/src/eval/codeblock.jl
index 93a6f5915..273b11100 100644
--- a/src/eval/codeblock.jl
+++ b/src/eval/codeblock.jl
@@ -9,8 +9,8 @@ $SIGNATURES
Take a fenced code block and return a tuple with the language, the relative
path (if any) and the code.
"""
-function parse_fenced_block(ss::SubString, shortcut=false)::Tuple
- if shortcut
+function parse_fenced_block(ss::SubString; shortcut=false, repl=false)::Tuple
+ if shortcut || repl
lang = locvar(:lang)::String
cntr = locvar(:fd_evalc)::Int
rpath = "_ceval_$cntr"
@@ -86,9 +86,9 @@ Helper function to process the content of a code block.
Return the html corresponding to the code block, possibly after having
evaluated the code.
"""
-function resolve_code_block(ss::SubString; shortcut=false)::String
+function resolve_code_block(ss::SubString; shortcut=false, repl=false)::String
# 1. what kind of code is it
- lang, rpath, code = parse_fenced_block(ss, shortcut)
+ lang, rpath, code = parse_fenced_block(ss; shortcut, repl)
# 1.a if no rpath is given, code should not be evaluated
isnothing(rpath) && return html_code(code, lang)
# 1.b if not julia code, eval is not supported
@@ -105,10 +105,11 @@ function resolve_code_block(ss::SubString; shortcut=false)::String
# languages, can't do the module trick so will need to keep track
# of that virtually. There will need to be a branching over lang=="julia"
# vs rest here.
+ repl_code_chunks = Pair{String,String}[]
# 2. here we have Julia code, assess whether to run it or not
# if not, just return the code as a html block
- if should_eval(code, rpath)
+ if shortcut || repl || should_eval(code, rpath)
# 3. here we have code that should be (re)evaluated
# >> retrieve the modulename, the module may not exist
# (& may not need to)
@@ -117,6 +118,7 @@ function resolve_code_block(ss::SubString; shortcut=false)::String
mod = ismodule(modname) ?
getfield(Main, Symbol(modname)) :
newmodule(modname)
+
# >> retrieve the code paths
cp = form_codepaths(rpath)
# >> write the code to file
@@ -129,22 +131,54 @@ function resolve_code_block(ss::SubString; shortcut=false)::String
out = ifelse(locvar(:auto_code_path)::Bool, cp.out_dir, bk)
isdir(out) || mkpath(out)
cd(out)
- # >> eval the code in the relevant module (this creates output/)
- res = run_code(mod, code, cp.out_path; strip_code=false)
- cd(bk)
- # >> write res to file
- # >> this weird thing with QuoteNode is to make sure that the proper
- # "show" method is called...
- io = IOBuffer()
- Core.eval(mod, quote show($(io), "text/plain", $(QuoteNode(res))) end)
- write(cp.res_path, take!(io))
+
+ if repl
+ # imitating https://github.com/JuliaLang/julia/blob/fe2eeadc0b382508bef7e77ab517789ea844e708/stdlib/REPL/src/REPL.jl#L429-L430
+ chunk_code = ""
+ chunk_ast = nothing
+ for line in split(code, r"\r?\n", keepempty=false)
+ chunk_code *= line * "\n"
+ chunk_ast = Base.parse_input_line(chunk_code)
+ if (isa(chunk_ast, Expr) && chunk_ast.head === :incomplete)
+ continue
+ else
+ # we have a complete chunk of code
+ # >> eval the code in the relevant module (this creates output/)
+ res = run_code(mod, chunk_code, cp.out_path; strip_code=false)
+ cd(bk)
+ # >> write res to string (see further down)
+ io = IOBuffer()
+ Core.eval(mod, quote show($(io), "text/plain", $(QuoteNode(res))) end)
+ push!(repl_code_chunks,
+ chunk_code => String(take!(io))
+ )
+ # reset for the next chunk
+ chunk_code = ""
+ chunk_ast = nothing
+ end
+ end
+ else
+ # >> eval the code in the relevant module (this creates output/)
+ res = run_code(mod, code, cp.out_path; strip_code=false)
+ cd(bk)
+ # >> write res to file
+ # >> this weird thing with QuoteNode is to make sure that the proper
+ # "show" method is called...
+ io = IOBuffer()
+ Core.eval(mod, quote show($(io), "text/plain", $(QuoteNode(res))) end)
+ write(cp.res_path, take!(io))
+ end
# >> since we've evaluated a code block, toggle scope as stale
set_var!(LOCAL_VARS, "fd_eval", true)
end
- # >> finally return as html
- if locvar(:showall)::Bool || shortcut
+ # >> finally return as html either with or without output
+ # --- with
+ if repl
+ return html_repl_code(repl_code_chunks)
+ elseif shortcut || locvar(:showall)::Bool
return html_code(code, lang) *
reprocess("\\show{$rpath}", [GLOBAL_LXDEFS["\\show"]])
end
+ # --- without
return html_code(code, lang)
end
diff --git a/src/parser/markdown/tokens.jl b/src/parser/markdown/tokens.jl
index 00b9bf5bb..8f46ebc4b 100644
--- a/src/parser/markdown/tokens.jl
+++ b/src/parser/markdown/tokens.jl
@@ -88,15 +88,16 @@ const MD_TOKENS = LittleDict{Char, Vector{TokenFinder}}(
'`' => [ isexactly("`", ('`',), false) => :CODE_SINGLE, # `⎵
isexactly("``", ('`',), false) => :CODE_DOUBLE, # ``⎵*
# 3+ can be named
- isexactly("```", SPACE_CHAR) => :CODE_TRIPLE, # ```⎵*
- isexactly("```!", SPACE_CHAR) => :CODE_TRIPLE!,# ```!⎵*
- is_language(3) => :CODE_LANG3, # ```lang*
- isexactly("````", SPACE_CHAR) => :CODE_QUAD, # ````⎵*
- is_language(4) => :CODE_LANG4, # ````lang*
- isexactly("`````", SPACE_CHAR) => :CODE_PENTA, # `````⎵*
- is_language(5) => :CODE_LANG5, # `````lang*
+ isexactly("```", SPACE_CHAR) => :CODE_TRIPLE, # ```⎵*
+ isexactly("```!", SPACE_CHAR) => :CODE_TRIPLE!, # ```!⎵*
+ isexactly("```>", SPACE_CHAR) => :CODE_REPL, # ```>⎵*
+ is_language(3) => :CODE_LANG3, # ```lang*
+ isexactly("````", SPACE_CHAR) => :CODE_QUAD, # ````⎵*
+ is_language(4) => :CODE_LANG4, # ````lang*
+ isexactly("`````", SPACE_CHAR) => :CODE_PENTA, # `````⎵*
+ is_language(5) => :CODE_LANG5, # `````lang*
],
- '*' => [ incrlook(is_hr3) => :HORIZONTAL_RULE,
+ '*' => [ incrlook(is_hr3) => :HORIZONTAL_RULE,
]
) # end dict
#= NOTE
@@ -148,6 +149,7 @@ const MD_OCB = [
OCProto(:CODE_BLOCK_LANG, :CODE_LANG4, (:CODE_QUAD,) ),
OCProto(:CODE_BLOCK_LANG, :CODE_LANG5, (:CODE_PENTA,) ),
OCProto(:CODE_BLOCK!, :CODE_TRIPLE!, (:CODE_TRIPLE,) ),
+ OCProto(:CODE_REPL, :CODE_REPL, (:CODE_TRIPLE,) ),
OCProto(:CODE_BLOCK, :CODE_TRIPLE, (:CODE_TRIPLE,) ),
OCProto(:CODE_BLOCK, :CODE_QUAD, (:CODE_QUAD,) ),
OCProto(:CODE_BLOCK, :CODE_PENTA, (:CODE_PENTA,) ),
@@ -256,7 +258,13 @@ CODE_BLOCKS_NAMES
List of names of code blocks environments.
"""
-const CODE_BLOCKS_NAMES = (:CODE_BLOCK_LANG, :CODE_BLOCK, :CODE_BLOCK!, :CODE_BLOCK_IND)
+const CODE_BLOCKS_NAMES = (
+ :CODE_BLOCK_LANG,
+ :CODE_BLOCK,
+ :CODE_BLOCK!,
+ :CODE_REPL,
+ :CODE_BLOCK_IND
+)
"""
MD_CLOSEP
diff --git a/src/regexes.jl b/src/regexes.jl
index 804c1934e..98b7f4b53 100644
--- a/src/regexes.jl
+++ b/src/regexes.jl
@@ -69,7 +69,7 @@ const FN_DEF_PAT = r"^\[\^[\p{L}0-9_]+\](:)?$"
CODE blocks
===================================================== =#
-const CODE_3!_PAT = r"```\!\s*\n?((?:.|\n)*)```"
+const CODE_3!_PAT = r"```(?:\!|\>)\s*\n?((?:.|\n)*)```"
const CODE_3_PAT = Regex(
"```([a-zA-Z][a-zA-Z-]*)" * # language
diff --git a/src/utils/html.jl b/src/utils/html.jl
index 609691ae3..f1caf4404 100644
--- a/src/utils/html.jl
+++ b/src/utils/html.jl
@@ -109,6 +109,23 @@ Convenience function to introduce inline code.
"""
html_code_inline(c::AS) = "$c
"
+
+"""
+ html_repl_code
+"""
+function html_repl_code(chunks::Vector{Pair{String,String}})::String
+ isempty(chunks) && return ""
+ io = IOBuffer()
+ println(io, "
")
+ for (code, result) in chunks
+ println(io, "julia> " * htmlesc(strip(code)))
+ println(io, result)
+ end
+ println(io, "
")
+ return String(take!(io))
+end
+
+
"""
html_err
diff --git a/test/eval/repl.jl b/test/eval/repl.jl
new file mode 100644
index 000000000..36d87f8d2
--- /dev/null
+++ b/test/eval/repl.jl
@@ -0,0 +1,27 @@
+@testset "parse fenced" begin
+ s = """
+ ```>
+ x = 5
+ y = 7 + x
+ ```
+ some explanation
+ ```>
+ z = y * 2
+ ```
+ """ |> fd2html
+
+ @test isapproxstr(s, """
+
+ julia> x = 5
+ 5
+ julia> y = 7 + x
+ 12
+
+
+ some explanation
+
+ julia> z = y * 2
+ 24
+
+ """)
+end
diff --git a/test/runtests.jl b/test/runtests.jl
index fc9b26206..7acb336cf 100644
--- a/test/runtests.jl
+++ b/test/runtests.jl
@@ -56,6 +56,7 @@ include("eval/codeblock.jl")
include("eval/eval.jl")
include("eval/integration.jl")
include("eval/extras.jl")
+include("eval/repl.jl")
# LATEX
println("LATEX")