Skip to content

Commit

Permalink
repl mode #818
Browse files Browse the repository at this point in the history
  • Loading branch information
tlienart committed Jul 9, 2023
1 parent 5629fc5 commit 8a91cb6
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 26 deletions.
1 change: 1 addition & 0 deletions src/converter/markdown/blocks.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
66 changes: 50 additions & 16 deletions src/eval/codeblock.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -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
Expand All @@ -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
26 changes: 17 additions & 9 deletions src/parser/markdown/tokens.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,) ),
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/regexes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
17 changes: 17 additions & 0 deletions src/utils/html.jl
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,23 @@ Convenience function to introduce inline code.
"""
html_code_inline(c::AS) = "<code>$c</code>"


"""
html_repl_code
"""
function html_repl_code(chunks::Vector{Pair{String,String}})::String
isempty(chunks) && return ""
io = IOBuffer()
println(io, "<pre><code class=\"language-julia julia-repl\">")
for (code, result) in chunks
println(io, "julia> " * htmlesc(strip(code)))
println(io, result)
end
println(io, "</code></pre>")
return String(take!(io))
end


"""
html_err
Expand Down
27 changes: 27 additions & 0 deletions test/eval/repl.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
@testset "parse fenced" begin
s = """
```>
x = 5
y = 7 + x
```
some explanation
```>
z = y * 2
```
""" |> fd2html

@test isapproxstr(s, """
<pre><code class="language-julia julia-repl">
julia> x &#61; 5
5
julia> y &#61; 7 &#43; x
12
</code></pre>
<p>some explanation</p>
<pre><code class="language-julia julia-repl">
julia> z &#61; y * 2
24
</code></pre>
""")
end
1 change: 1 addition & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down

0 comments on commit 8a91cb6

Please sign in to comment.