dynamic YAML option specification

avi/dynamic
Shuhei Kadowaki 2020-05-15 23:51:52 +09:00
parent 95c3dac962
commit 492d1f330b
8 changed files with 85 additions and 96 deletions

View File

@ -18,7 +18,7 @@ function format(doc::WeaveDoc)
docformat.formatdict[:cwd] = doc.cwd # pass wd to figure formatters
docformat.formatdict[:theme] = doc.highlight_theme
strip_header!(doc)
restore_header!(doc)
for chunk in copy(doc.chunks)
result = format_chunk(chunk, formatdict, docformat)
@ -111,27 +111,20 @@ function render_doc(formatted, doc, format::JMarkdown2tex)
)
end
strip_header!(doc::WeaveDoc) = strip_header!(doc.chunks[1], doc.doctype)
function strip_header!(docchunk::DocChunk, doctype)
doctype == "pandoc" && return
content = docchunk.content[1].content
if (m = match(HEADER_REGEX, content)) !== nothing
# TODO: is there other format where we want to keep headers ?
docchunk.content[1].content = if doctype != "github"
lstrip(replace(content, HEADER_REGEX => ""))
else
# only strips Weave headers
header = YAML.load(m[:header])
delete!(header, WEAVE_OPTION_NAME)
if isempty(header)
lstrip(replace(content, HEADER_REGEX => ""))
else
lstrip(replace(content, HEADER_REGEX => "---\n$(YAML.write(header))---"))
end
end
end
function restore_header!(doc)
doctype = doc.doctype
# TODO: is there any other format where we want to restore headers ?
doctype "github" && return
# only strips Weave headers
delete!(doc.header, WEAVE_OPTION_NAME)
isempty(doc.header) && return
# restore remained headers as `DocChunk`
header_text = "---\n$(YAML.write(doc.header))---"
pushfirst!(doc.chunks, DocChunk(header_text, 0, 0))
end
strip_header!(codechunk::CodeChunk, doctype) = return
function format_chunk(chunk::DocChunk, formatdict, docformat)
return join([format_inline(c) for c in chunk.content], "")

View File

@ -2,18 +2,13 @@ using JSON, YAML
function WeaveDoc(source, format::Union{Nothing,AbstractString} = nothing)
document = replace(read(source, String), "\r\n" => "\n") # normalize line ending
isnothing(format) && (format = detect_informat(source))
chunks = parse_doc(document, format)
return WeaveDoc(source, chunks)
end
function WeaveDoc(source, chunks::Vector{WeaveChunk})
path, fname = splitdir(abspath(source))
basename = splitext(fname)[1]
header = parse_header(first(chunks))
# get chunk defaults from header and update
isnothing(format) && (format = detect_informat(source))
header, chunks = parse_doc(read(source, String), format)
# update default chunk options from header
chunk_defaults = deepcopy(get_chunk_defaults())
if haskey(header, WEAVE_OPTION_NAME)
for key in keys(chunk_defaults)
@ -55,18 +50,17 @@ function detect_informat(source::AbstractString)
return "noweb"
end
function parse_doc(document, format)::Vector{WeaveChunk}
return if format == "markdown"
parse_markdown(document)
elseif format == "noweb"
parse_markdown(document, true)
elseif format == "script"
parse_script(document)
elseif format == "notebook"
parse_notebook(document)
else
function parse_doc(document, format)
document = replace(document, "\r\n" => "\n") # normalize line ending
header_text, document = separete_header_text(document)
return parse_header(header_text),
format == "markdown" ? parse_markdown(document) :
format == "noweb" ? parse_markdown(document, true) :
format == "script" ? parse_script(document) :
format == "notebook" ? parse_notebook(document) :
error("unsupported format given: $(format)")
end
end
function pushopt(options::Dict, expr::Expr)
@ -84,12 +78,13 @@ function DocChunk(text::AbstractString, number, start_line; notebook = false)
return DocChunk(content, number, start_line)
end
const INLINE_REGEX = r"`j\s+(.*?)`|^!\s(.*)$"m
const INLINE_REGEX = r"`j\s+(.*?)`"
const INLINE_REGEXES = r"`j\s+(.*?)`|^!\s(.*)$"m
function parse_inlines(text)::Vector{Inline}
occursin(INLINE_REGEX, text) || return parse_inline(text)
occursin(INLINE_REGEXES, text) || return parse_inline(text)
inline_chunks = eachmatch(INLINE_REGEX, text)
inline_chunks = eachmatch(INLINE_REGEXES, text)
s = 1
e = 1
res = Inline[]
@ -118,20 +113,28 @@ parse_inline(text) = Inline[InlineText(text, 1, length(text), 1)]
# headers
# -------
parse_header(chunk::CodeChunk) = Dict()
const HEADER_REGEX = r"^---$(?<header>((?!---).)+)^---$"ms
function parse_header(chunk::DocChunk)
m = match(HEADER_REGEX, chunk.content[1].content)
if m !== nothing
header = YAML.load(string(m[:header]))
else
header = Dict()
end
return header
# TODO: non-Weave headers should keep live in a doc
# separates header section from `text`
function separete_header_text(text)
m = match(HEADER_REGEX, text)
isnothing(m) && return "", text
return m[:header], replace(text, HEADER_REGEX => "")
end
# HACK:
# YAML.jl can't parse text including ``` characters, so first replace all the inline code
# with these temporary code start/end string
const HEADER_INLINE_START = "<weave_header_inline_start>"
const HEADER_INLINE_END = "<weave_header_inline_end>"
function parse_header(header_text)
isempty(header_text) && return Dict()
pat = INLINE_REGEX => SubstitutionString("$(HEADER_INLINE_START)\\1$(HEADER_INLINE_END)")
header_text = replace(header_text, pat)
return YAML.load(header_text)
end
include("markdown.jl")
include("script.jl")

View File

@ -100,6 +100,8 @@ function run_doc(
executed = [executed; result_chunks]
end
replace_header_inline!(doc, report, mod) # evaluate and replace inline code in header
doc.header_script = report.header_script
doc.chunks = executed
@ -237,17 +239,11 @@ function capture_output(expr, SandBox::Module, term, disp, lastline, throw_error
reader = @async read(rw, String)
try
obj = Core.eval(SandBox, expr)
if (term || disp) && (typeof(expr) != Expr || expr.head != :toplevel)
isnothing(obj) || display(obj)
# This shows images and lone variables, result can
# Handle last line sepately
elseif lastline && !isnothing(obj)
(typeof(expr) != Expr || expr.head != :toplevel) && display(obj)
end
catch E
throw_errors && throw(E)
display(E)
@warn("ERROR: $(typeof(E)) occurred, including output in Weaved document")
!isnothing(obj) && ((term || disp) || lastline) && display(obj)
catch err
throw_errors && throw(err)
display(err)
@warn "ERROR: $(typeof(err)) occurred, including output in Weaved document"
finally
redirect_stdout(oldSTDOUT)
close(wr)
@ -486,3 +482,25 @@ function collect_results(chunk::CodeChunk, fmt::CollectResult)
end
return [chunk]
end
const HEADER_INLINE = Regex("$(HEADER_INLINE_START)(?<code>.+)$(HEADER_INLINE_END)")
replace_header_inline!(doc, report, mod) = _replace_header_inline!(doc, doc.header, report, mod)
function _replace_header_inline!(doc, header, report, mod)
replace!(header) do (k,v)
return k => v isa Dict ?
_replace_header_inline!(doc, v, report, mod) :
replace(v, HEADER_INLINE => s -> begin
m = match(HEADER_INLINE, s)
run_inline_code(m[:code], doc, report, mod)
end)
end
return header
end
function run_inline_code(s, doc, report, mod)
inline = InlineCode(s, 1, 1, 1, :inline)
inline = run_inline(inline, doc, report, mod)
return strip(inline.output, '"')
end

View File

@ -1,7 +1,6 @@
<div class="Random plot">
<p>Some inline output</p>

View File

@ -1,7 +1,6 @@
\begin{frame}[fragile]
\frametitle{Random plot}

View File

@ -51,33 +51,6 @@ doc.template = "templates/mini.tpl"
rendered = Weave.render_doc("Hello", doc)
@test rendered == "\nHello\n"
# Test header parsing and stripping
header = """
---
title : Test block
author : Matti Pastell
---
# Actual header
and some text
"""
dchunk = Weave.DocChunk(header, 1, 1)
h = Weave.parse_header(dchunk)
h_ref = Dict("author" => "Matti Pastell", "title" => "Test block")
@test h_ref == h
Weave.strip_header!(dchunk, "md2html")
h_ref = """
# Actual header
and some text
"""
@test dchunk.content[1].content == h_ref
# Test wrapping
cows = repeat("🐄", 100)

View File

@ -12,7 +12,7 @@ Some markdown with inline stuff and `j code`
"""
ms = collect(eachmatch(Weave.INLINE_REGEX, doc))
ms = collect(eachmatch(Weave.INLINE_REGEXES, doc))
@test ms[1][2] == "println(\"Something\")"
@test ms[2][1] == "code"
@test ms[3][1] == "show(\"is\")"

View File

@ -6,7 +6,11 @@ using Weave: run_doc
# TODO: add test for `include_weave`
# constructs `WeaveDoc` from `String`
mock_doc(str, chunk_parser = Weave.parse_markdown) = Weave.WeaveDoc("dummy", chunk_parser(str))
function mock_doc(str, format = "markdown")
f = tempname()
write(f, str)
return Weave.WeaveDoc(f, format)
end
@testset "Weave" begin