diff --git a/src/converter/md.jl b/src/converter/md.jl index c5a202108..137eb69c8 100644 --- a/src/converter/md.jl +++ b/src/converter/md.jl @@ -86,6 +86,13 @@ function convert_md(mds::String, pre_lxdefs::Vector{LxDef}=Vector{LxDef}(); lxcontext = LxContext(lxcoms, lxdefs, braces) hstring = convert_inter_html(inter_html, mblocks, lxcontext) + #> if there's code, assemble it so that can be shown or loaded in one shot + codes = LOCAL_PAGE_VARS["jd_code_scope"].first.codes + if !isempty(codes) + set_var!(LOCAL_PAGE_VARS, "jd_code", + strip(prod(c*"\n" for c in codes))) + end + # Return the string + judoc variables return hstring, jd_vars end diff --git a/src/converter/md_blocks.jl b/src/converter/md_blocks.jl index a669255e6..01c9ce742 100644 --- a/src/converter/md_blocks.jl +++ b/src/converter/md_blocks.jl @@ -9,7 +9,7 @@ function convert_block(β::AbstractBlock, lxcontext::LxContext)::AS β isa HTML_SPCH && return ifelse(isempty(β.r), β.ss, β.r) # Return relevant interpolated string based on case βn = β.name - βn ∈ MD_HEADER && return convert_header(β) + βn ∈ MD_HEADER && return convert_header(β) βn == :CODE_INLINE && return html_code_inline(content(β) |> htmlesc) βn == :CODE_BLOCK_LANG && return convert_code_block(β.ss) βn == :CODE_BLOCK_IND && return convert_indented_code_block(β.ss) @@ -131,19 +131,26 @@ const MESSAGE_FILE_GEN = "# This file was generated by JuDoc, do not modify it. """Convenience function to increment the eval' code block counter""" increment_code_head() = (LOCAL_PAGE_VARS["jd_code_head"].first[] += 1) +"""Convenience function to mark `jd_code_eval` as true (subsequent blocks will be evaled)""" +toggle_jd_code_eval() = (LOCAL_PAGE_VARS["jd_code_eval"].first[] = true) + """ $SIGNATURES Helper function to eval a code block, write it where appropriate, and finally return a resolved block that can be displayed in the html. """ -function eval_and_resolve_code(code::AS, rpath::AS; eval::Bool=true)::String +function eval_and_resolve_code(code::AS, rpath::AS; + eval::Bool=true, nopush::Bool=false)::String # Here we have a julia code block that was provided with a script path # It will consequently be # 1. written to script file unless it's already there # 2. eval with redirect (unless file+output already there) # 3. inserted after cleaning out lines (see resolve_lx_input) + # start by adding it to code scope + nopush || push!(LOCAL_PAGE_VARS["jd_code_scope"].first, rpath, code) + # form the path path = resolve_assets_rpath(rpath; canonical=true, code=true) @@ -164,7 +171,7 @@ function eval_and_resolve_code(code::AS, rpath::AS; eval::Bool=true)::String end write(path, MESSAGE_FILE_GEN * code) - print(rpad("\r→ evaluating code [...] ($(CUR_PATH[]), $rpath)", 79)*"\r") + print(rpad("\r→ evaluating code [...] ($(CUR_PATH[]), $rpath)", 79)) # - execute the code while redirecting stdout to file Logging.disable_logging(Logging.LogLevel(3_000)) open(out_path, "w") do outf # for stdout @@ -208,9 +215,17 @@ function convert_code_block(ss::SubString)::String # path currently has an indicative `:` we don't care about rpath = rpath[2:end] + # extract handles of relevant local page variables + reeval = LOCAL_PAGE_VARS["reeval"].first # full page re-eval + eval = LOCAL_PAGE_VARS["jd_code_eval"].first[] # eval toggle from given point + freeze = LOCAL_PAGE_VARS["freezecode"].first + scope = LOCAL_PAGE_VARS["jd_code_scope"].first + head = increment_code_head() + # In the case of forced re-eval, we don't care about the # code scope just force-reeval everything sequentially - if FORCE_REEVAL[] || LOCAL_PAGE_VARS["reeval"].first + if FORCE_REEVAL[] || reeval || eval + length(scope.codes) ≥ head && purgefrom!(scope, head) return eval_and_resolve_code(code, rpath) end @@ -219,7 +234,8 @@ function convert_code_block(ss::SubString)::String # the case then there will be a check to see if the relevant # files exist, if they don't exist the code *will* be eval'ed # (see `eval_and_resolve_code`) - if FULL_PASS[] || LOCAL_PAGE_VARS["freezecode"].first + if FULL_PASS[] || freeze + length(scope.codes) ≥ head && purgefrom!(scope, head) return eval_and_resolve_code(code, rpath, eval=false) end @@ -227,40 +243,40 @@ function convert_code_block(ss::SubString)::String # A. full pass but with forced-reeval # B. local pass with non-frozen code - # handle for dictionary of eval'ed code blocks (scope) - code_scope = LOCAL_PAGE_VARS["jd_code_scope"].first - # check if the page we're looking at is in scope if CUR_PATH[] != CUR_PATH_WITH_EVAL[] # we're necessarily at the first code block of the page. # need to re-instantiate a code scope; note that if we # are here then necessarily a def_LOCAL_PAGE_VARS was # called, so LOCAL_PAGE_VARS["jd_code_head"] points to 1 - reset!(code_scope, rpath, code) + reset!(scope) # keep track that the page is now in scope CUR_PATH_WITH_EVAL[] = CUR_PATH[] + # flag rest of page as to be eval-ed (might be stale) + toggle_jd_code_eval() + # eval and resolve code return eval_and_resolve_code(code, rpath) end # we're in scope, compare the code block with the # current code scope and act appropriately - head = increment_code_head() - ncodes = length(code_scope.codes) + ncodes = length(scope.codes) # there is only one case where we might not add and eval # --> if c ≤ length(code_dict) -- code block may be among seen ones # --> code == code_dict[rpath] -- the content matches exactly if (head ≤ ncodes) - if (code_scope.rpaths[head] == rpath && code == code_scope.codes[head]) + if (scope.rpaths[head] == rpath && code == scope.codes[head]) # resolve with no eval (the function will check if the files are # present and if they're not, there will be an evaluation) - return eval_and_resolve_code(code, rpath; eval=false) + return eval_and_resolve_code(code, rpath; eval=false, nopush=true) else - # all further blocks are stale - purgeafter!(code_scope, head-1) + # purge subsequent code blocks as stale + purgefrom!(scope, head) + # flag rest of page as to be eval-ed (stale) + toggle_jd_code_eval() end end - push!(code_scope, rpath, code) return eval_and_resolve_code(code, rpath) end diff --git a/src/jd_vars.jl b/src/jd_vars.jl index 4caab54d5..c75be6409 100644 --- a/src/jd_vars.jl +++ b/src/jd_vars.jl @@ -10,7 +10,6 @@ DEVNOTE: marked as constant for perf reasons but can be modified since Dict. """ const GLOBAL_PAGE_VARS = PageVars() - """ $(SIGNATURES) @@ -50,16 +49,16 @@ function push!(cs::CodeScope, rpath::SubString, code::SubString)::Nothing end """Convenience function to (re)start a code scope.""" -function reset!(cs::CodeScope, rpath::SubString, code::SubString)::Nothing - cs.rpaths = [rpath] - cs.codes = [code] +function reset!(cs::CodeScope)::Nothing + cs.rpaths = [] + cs.codes = [] return nothing end -"""Convenience function to clear arrays beyond an index""" -function purgeafter!(cs::CodeScope, head::Int)::Nothing - cs.rpaths = cs.rpaths[1:head] - cs.codes = cs.codes[1:head] +"""Convenience function to purge code scope from head""" +function purgefrom!(cs::CodeScope, head::Int) + cs.rpaths = cs.rpaths[1:head-1] + cs.codes = cs.codes[1:head-1] return nothing end @@ -126,8 +125,10 @@ is processed. # Internal vars for code blocks LOCAL_PAGE_VARS["jd_code_scope"] = code_scope - LOCAL_PAGE_VARS["jd_code_head"] = Pair(Ref(0), (Ref{Int},)) - LOCAL_PAGE_VARS["reeval"] = Pair(false, (Bool,)) # whether to always re-evals all on pg + LOCAL_PAGE_VARS["jd_code_head"] = Pair(Ref(0), (Ref{Int},)) + LOCAL_PAGE_VARS["jd_code_eval"] = Pair(Ref(false), (Ref{Bool},)) # toggle reeval + LOCAL_PAGE_VARS["reeval"] = Pair(false, (Bool,)) # always reeval on pg + LOCAL_PAGE_VARS["jd_code"] = Pair("", (String,)) # just the script # If there are GLOBAL vars that are defined, they take precedence local_keys = keys(LOCAL_PAGE_VARS) diff --git a/test/global/eval.jl b/test/global/eval.jl index b1cdd6bb1..792197b39 100644 --- a/test/global/eval.jl +++ b/test/global/eval.jl @@ -143,6 +143,13 @@ end
8
""") + @test J.LOCAL_PAGE_VARS["jd_code"].first == """ + a = 5 + println(a) + + a += 3 + println(a)""" + h = raw""" @def hascode = true @def reeval = true @@ -174,4 +181,13 @@ end println(a)
9
""") + @test J.LOCAL_PAGE_VARS["jd_code"].first == """ + a = 5 + println(a) + + a += 1 + println(a) + + a += 3 + println(a)""" end