mirror of https://github.com/mpastell/Weave.jl
commit
090ad87ed9
|
@ -120,3 +120,20 @@ Weave will remove the first empty space from each line of documentation.
|
|||
|
||||
|
||||
[See sample document:](https://github.com/mpastell/Weave.jl/blob/master/examples/FIR_design.jl)
|
||||
|
||||
## Inline code
|
||||
|
||||
You can also add inline code to your documents using
|
||||
|
||||
```
|
||||
`j juliacode`
|
||||
```
|
||||
|
||||
syntax. The code will be replaced with the output of running the code.
|
||||
If the code produces figures the filename or base64 encoded string will be
|
||||
added to output e.g. to include a Plots figure in markdown you can use:
|
||||
|
||||
```
|
||||
![A plot](`j plot(1:10)`)
|
||||
```
|
||||
|
||||
|
|
75
src/Weave.jl
75
src/Weave.jl
|
@ -87,46 +87,49 @@ function weave(source ; doctype = :auto, plotlib=:auto,
|
|||
css != nothing && (doc.css = css)
|
||||
template != nothing && (doc.template = template)
|
||||
|
||||
try
|
||||
doc = run(doc, doctype = doctype, plotlib=plotlib,
|
||||
out_path=out_path, args = args,
|
||||
fig_path = fig_path, fig_ext = fig_ext, cache_path = cache_path, cache=cache)
|
||||
formatted = format(doc)
|
||||
|
||||
doc = run(doc, doctype = doctype, plotlib=plotlib,
|
||||
out_path=out_path, args = args,
|
||||
fig_path = fig_path, fig_ext = fig_ext, cache_path = cache_path, cache=cache)
|
||||
formatted = format(doc)
|
||||
outname = get_outname(out_path, doc)
|
||||
|
||||
outname = get_outname(out_path, doc)
|
||||
open(outname, "w") do io
|
||||
write(io, formatted)
|
||||
end
|
||||
|
||||
open(outname, "w") do io
|
||||
write(io, formatted)
|
||||
#Special for that need external programs
|
||||
if doc.doctype == "pandoc2html"
|
||||
mdname = outname
|
||||
outname = get_outname(out_path, doc, ext = "html")
|
||||
pandoc2html(formatted, doc, outname)
|
||||
rm(mdname)
|
||||
elseif doc.doctype == "pandoc2pdf"
|
||||
mdname = outname
|
||||
outname = get_outname(out_path, doc, ext = "pdf")
|
||||
pandoc2pdf(formatted, doc, outname)
|
||||
rm(mdname)
|
||||
elseif doc.doctype == "md2pdf"
|
||||
success = run_latex(doc, outname, latex_cmd)
|
||||
success || return
|
||||
outname = get_outname(out_path, doc, ext = "pdf")
|
||||
end
|
||||
|
||||
|
||||
doc.cwd == pwd() && (outname = basename(outname))
|
||||
info("Report weaved to $outname")
|
||||
catch e
|
||||
warn("Something went wrong during weaving")
|
||||
print(e)
|
||||
finally
|
||||
doctype == :auto && (doctype = detect_doctype(doc.source))
|
||||
if contains(doctype, "2pdf") && cache == :off
|
||||
rm(doc.fig_path, force = true, recursive = true)
|
||||
elseif contains(doctype, "2html")
|
||||
rm(doc.fig_path, force = true, recursive = true)
|
||||
end
|
||||
end
|
||||
|
||||
#Special for that need external programs
|
||||
if doc.doctype == "pandoc2html"
|
||||
mdname = outname
|
||||
outname = get_outname(out_path, doc, ext = "html")
|
||||
pandoc2html(formatted, doc, outname)
|
||||
rm(mdname)
|
||||
elseif doc.doctype == "pandoc2pdf"
|
||||
mdname = outname
|
||||
outname = get_outname(out_path, doc, ext = "pdf")
|
||||
pandoc2pdf(formatted, doc, outname)
|
||||
rm(mdname)
|
||||
elseif doc.doctype == "md2pdf"
|
||||
success = run_latex(doc, outname, latex_cmd)
|
||||
success || return
|
||||
outname = get_outname(out_path, doc, ext = "pdf")
|
||||
end
|
||||
|
||||
doctype == :auto && (doctype = detect_doctype(doc.source))
|
||||
|
||||
if contains(doctype, "2pdf") && cache == :off
|
||||
rm(doc.fig_path, force = true, recursive = true)
|
||||
elseif contains(doctype, "2html")
|
||||
rm(doc.fig_path, force = true, recursive = true)
|
||||
end
|
||||
|
||||
doc.cwd == pwd() && (outname = basename(outname))
|
||||
|
||||
info("Report weaved to $outname")
|
||||
end
|
||||
|
||||
function weave(doc::AbstractString, doctype::AbstractString)
|
||||
|
|
27
src/cache.jl
27
src/cache.jl
|
@ -33,7 +33,28 @@ function restore_chunk(chunk::CodeChunk, cached)
|
|||
return new_chunks
|
||||
end
|
||||
|
||||
#Could be used to restore inline code in future
|
||||
function restore_chunk(chunk::DocChunk, cached)
|
||||
return chunk
|
||||
#Restore inline code
|
||||
function restore_chunk(chunk::DocChunk, cached::WeaveDoc)
|
||||
#Get chunk from cached doc
|
||||
c_chunk = filter(x -> x.number == chunk.number &&
|
||||
isa(x, DocChunk), cached.chunks)
|
||||
isempty(c_chunk) && return chunk
|
||||
c_chunk = c_chunk[1]
|
||||
|
||||
#Collect cached code
|
||||
c_inline = filter(x -> isa(x, InlineCode), c_chunk.content)
|
||||
isempty(c_inline) && return chunk
|
||||
|
||||
#Restore cached results for Inline code
|
||||
n = length(chunk.content)
|
||||
for i in 1:n
|
||||
if isa(chunk.content[i], InlineCode)
|
||||
ci = filter(x -> x.number == chunk.content[i].number, c_inline)
|
||||
isempty(ci) && continue
|
||||
chunk.content[i].output = ci[1].output
|
||||
chunk.content[i].rich_output = ci[1].rich_output
|
||||
chunk.content[i].figures = ci[1].figures
|
||||
end
|
||||
end
|
||||
return chunk
|
||||
end
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
|
||||
abstract WeaveChunk
|
||||
abstract Inline
|
||||
|
||||
type WeaveDoc
|
||||
source::AbstractString
|
||||
basename::AbstractString
|
||||
path::AbstractString
|
||||
chunks::Array
|
||||
chunks::Array{WeaveChunk}
|
||||
cwd::AbstractString
|
||||
format
|
||||
doctype::AbstractString
|
||||
|
@ -29,7 +32,7 @@ immutable ChunkOutput
|
|||
figures::Array{AbstractString}
|
||||
end
|
||||
|
||||
type CodeChunk
|
||||
type CodeChunk <: WeaveChunk
|
||||
content::AbstractString
|
||||
number::Int
|
||||
result_no::Int
|
||||
|
@ -45,10 +48,34 @@ type CodeChunk
|
|||
end
|
||||
end
|
||||
|
||||
type DocChunk
|
||||
content::AbstractString
|
||||
type DocChunk <: WeaveChunk
|
||||
content::Array{Inline}
|
||||
number::Int
|
||||
start_line::Int
|
||||
function DocChunk(text::AbstractString, number::Int, start_line::Int, inline_regex = nothing)
|
||||
chunks = parse_inline(text, inline_regex)
|
||||
new(chunks, number, start_line)
|
||||
end
|
||||
end
|
||||
|
||||
type InlineText <: Inline
|
||||
content::AbstractString
|
||||
si::Int
|
||||
ei::Int
|
||||
number::Int
|
||||
end
|
||||
|
||||
type InlineCode <: Inline
|
||||
content::AbstractString
|
||||
si::Int
|
||||
ei::Int
|
||||
number::Int
|
||||
output::AbstractString
|
||||
rich_output::AbstractString
|
||||
figures::Array{AbstractString}
|
||||
function InlineCode(content, si, ei, number)
|
||||
new(content, si, ei, number, "", "", AbstractString[])
|
||||
end
|
||||
end
|
||||
|
||||
type TermResult
|
||||
|
|
|
@ -105,22 +105,32 @@ function get_titleblock(doc::WeaveDoc)
|
|||
end
|
||||
|
||||
function strip_header(chunk::DocChunk)
|
||||
if ismatch(r"^---$(?<header>.+)^---$"ms, chunk.content)
|
||||
chunk.content = lstrip(replace(chunk.content, r"^---$(?<header>.+)^---$"ms, ""))
|
||||
if ismatch(r"^---$(?<header>.+)^---$"ms, chunk.content[1].content)
|
||||
chunk.content[1].content = lstrip(replace(chunk.content[1].content, r"^---$(?<header>.+)^---$"ms, ""))
|
||||
end
|
||||
return chunk
|
||||
end
|
||||
|
||||
function format_chunk(chunk::DocChunk, formatdict, docformat)
|
||||
return chunk.content
|
||||
return join([format_inline(c) for c in chunk.content], "")
|
||||
end
|
||||
|
||||
function format_inline(inline::InlineText)
|
||||
return inline.content
|
||||
end
|
||||
|
||||
function format_inline(inline::InlineCode)
|
||||
isempty(inline.rich_output) || return inline.rich_output
|
||||
isempty(inline.figures) || return inline.figures[end]
|
||||
isempty(inline.output) || return inline.output
|
||||
end
|
||||
|
||||
function format_chunk(chunk::DocChunk, formatdict, docformat::JMarkdown2HTML)
|
||||
m = Base.Markdown.parse(chunk.content)
|
||||
text = format_chunk(chunk, formatdict, nothing)
|
||||
m = Base.Markdown.parse(text)
|
||||
return string(Documenter.Writers.HTMLWriter.mdconvert(m))
|
||||
end
|
||||
|
||||
|
||||
#Fixes to Base latex writer
|
||||
function Base.Markdown.latex(io::IO, md::Base.Markdown.Paragraph)
|
||||
println(io)
|
||||
|
@ -144,7 +154,8 @@ end
|
|||
|
||||
|
||||
function format_chunk(chunk::DocChunk, formatdict, docformat::JMarkdown2tex)
|
||||
m = Base.Markdown.parse(chunk.content)
|
||||
text = format_chunk(chunk, formatdict, nothing)
|
||||
m = Base.Markdown.parse(text)
|
||||
return Base.Markdown.latex(m)
|
||||
end
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ pushopt(options::Dict,expr::Expr) = Base.Meta.isexpr(expr,:(=)) && (options[expr
|
|||
type MarkupInput
|
||||
codestart::Regex
|
||||
codeend::Regex
|
||||
inline::Regex
|
||||
end
|
||||
|
||||
type ScriptInput
|
||||
|
@ -12,23 +13,29 @@ type ScriptInput
|
|||
doc_start::Regex
|
||||
opt_line::Regex
|
||||
opt_start::Regex
|
||||
inline::Regex
|
||||
end
|
||||
|
||||
type NotebookInput
|
||||
inline
|
||||
end
|
||||
|
||||
const input_formats = Dict{AbstractString, Any}(
|
||||
"noweb" => MarkupInput(r"^<<(.*?)>>=\s*$",
|
||||
r"^@\s*$"),
|
||||
r"^@\s*$",
|
||||
r"`j\s+(.*?)`"s
|
||||
),
|
||||
"markdown" => MarkupInput(
|
||||
r"^[`~]{3,}(?:\{|\{\.|)julia(?:;|)\s*(.*?)(\}|\s*)$",
|
||||
r"^[`~]{3,}\s*$"),
|
||||
r"^[`~]{3,}\s*$",
|
||||
r"`j\s+(.*?)`"s),
|
||||
"script" => ScriptInput(
|
||||
r"(^#'.*)|(^#%%.*)|(^# %%.*)",
|
||||
r"(^#')|(^#%%)|(^# %%)",
|
||||
r"(^#\+.*$)|(^#%%\+.*$)|(^# %%\+.*$)",
|
||||
r"(^#\+)|(^#%%\+)|(^# %%\+)"),
|
||||
"notebook" => NotebookInput()
|
||||
r"(^#\+)|(^#%%\+)|(^# %%\+)",
|
||||
r"`j\s+(.*?)`"s),
|
||||
"notebook" => NotebookInput(nothing) #Don't parse inline code from notebooks
|
||||
)
|
||||
|
||||
"""Detect the input format based on file extension"""
|
||||
|
@ -59,7 +66,7 @@ function parse_header(chunk::CodeChunk)
|
|||
end
|
||||
|
||||
function parse_header(chunk::DocChunk)
|
||||
m = match(r"^---$(?<header>.+)^---$"ms, chunk.content)
|
||||
m = match(r"^---$(?<header>.+)^---$"ms, chunk.content[1].content)
|
||||
if m !== nothing
|
||||
header = YAML.load(string(m[:header]))
|
||||
else
|
||||
|
@ -109,7 +116,7 @@ function parse_doc(document::AbstractString, format::MarkupInput)
|
|||
haskey(options, :name) || (options[:name] = nothing)
|
||||
|
||||
if !isempty(strip(content))
|
||||
chunk = DocChunk(content, docno, start_line)
|
||||
chunk = DocChunk(content, docno, start_line, format.inline)
|
||||
docno += 1
|
||||
push!(parsed, chunk)
|
||||
end
|
||||
|
@ -143,7 +150,7 @@ function parse_doc(document::AbstractString, format::MarkupInput)
|
|||
|
||||
#Remember the last chunk
|
||||
if strip(content) != ""
|
||||
chunk = DocChunk(content, docno, start_line)
|
||||
chunk = DocChunk(content, docno, start_line, format.inline)
|
||||
#chunk = Dict{Symbol,Any}(:type => "doc", :content => content,
|
||||
# :number => docno, :start_line => start_line)
|
||||
push!(parsed, chunk)
|
||||
|
@ -223,7 +230,7 @@ function parse_doc(document::AbstractString, format::ScriptInput)
|
|||
elseif state == "doc" && strip(line) != "" && strip(read) != ""
|
||||
state = "code"
|
||||
(docno > 1) && (read = "\n" * read) # Add whitespace to doc chunk. Needed for markdown output
|
||||
chunk = DocChunk(read, docno, start_line)
|
||||
chunk = DocChunk(read, docno, start_line, format.inline)
|
||||
push!(parsed, chunk)
|
||||
options = Dict{Symbol,Any}()
|
||||
start_line = lineno
|
||||
|
@ -244,7 +251,7 @@ function parse_doc(document::AbstractString, format::ScriptInput)
|
|||
chunk = CodeChunk("\n" * strip(read), codeno, start_line, optionString, options)
|
||||
push!(parsed, chunk)
|
||||
else
|
||||
chunk = DocChunk(read, docno, start_line)
|
||||
chunk = DocChunk(read, docno, start_line, format.inline)
|
||||
push!(parsed, chunk)
|
||||
end
|
||||
|
||||
|
@ -277,3 +284,32 @@ function parse_doc(document::String, format::NotebookInput)
|
|||
|
||||
return parsed
|
||||
end
|
||||
|
||||
#Use this if regex is undefined
|
||||
function parse_inline(text, noex)
|
||||
return Inline[InlineText(text, 1, length(text), 1)]
|
||||
end
|
||||
|
||||
function parse_inline(text::AbstractString, inline_ex::Regex)
|
||||
ismatch(inline_ex, text) || return Inline[InlineText(text, 1, length(text), 1)]
|
||||
|
||||
inline_chunks = eachmatch(inline_ex, text)
|
||||
s = 1
|
||||
e = 1
|
||||
res = Inline[]
|
||||
textno = 1
|
||||
codeno = 1
|
||||
|
||||
for ic in inline_chunks
|
||||
s = ic.offset
|
||||
doc = InlineText(text[e:(s-1)], e, s-1, textno)
|
||||
textno += 1
|
||||
push!(res, doc)
|
||||
e = s + length(ic.match)
|
||||
push!(res, InlineCode(ic.captures[1], s, e, codeno))
|
||||
codeno += 1
|
||||
end
|
||||
push!(res, InlineText(text[e:end], e, length(text), textno))
|
||||
|
||||
return res
|
||||
end
|
32
src/run.jl
32
src/run.jl
|
@ -73,7 +73,7 @@ function Base.run(doc::WeaveDoc; doctype = :auto, plotlib=:auto,
|
|||
for i = 1:n
|
||||
chunk = doc.chunks[i]
|
||||
|
||||
if typeof(chunk) == CodeChunk
|
||||
if isa(chunk, CodeChunk)
|
||||
options = merge(rcParams[:chunk_defaults], chunk.options)
|
||||
merge!(chunk.options, options)
|
||||
end
|
||||
|
@ -84,7 +84,7 @@ function Base.run(doc::WeaveDoc; doctype = :auto, plotlib=:auto,
|
|||
result_chunks = restore_chunk(chunk, cached)
|
||||
else
|
||||
|
||||
result_chunks = run_chunk(chunk, report, SandBox)
|
||||
result_chunks = run_chunk(chunk, report, SandBox)
|
||||
end
|
||||
|
||||
executed = [executed; result_chunks]
|
||||
|
@ -119,6 +119,7 @@ end
|
|||
|
||||
|
||||
function run_chunk(chunk::CodeChunk, report::Report, SandBox::Module)
|
||||
info("Weaving chunk $(chunk.number) from line $(chunk.start_line)")
|
||||
result_chunks = eval_chunk(chunk, report, SandBox)
|
||||
contains(report.formatdict[:doctype], "2html") && (result_chunks = embed_figures(result_chunks, report.cwd))
|
||||
return result_chunks
|
||||
|
@ -155,9 +156,31 @@ function img2base64(fig, cwd)
|
|||
end
|
||||
|
||||
function run_chunk(chunk::DocChunk, report::Report, SandBox::Module)
|
||||
chunk.content = [run_inline(c, report, SandBox) for c in chunk.content]
|
||||
return chunk
|
||||
end
|
||||
|
||||
function run_inline(inline::InlineText, report::Report, SandBox::Module)
|
||||
return inline
|
||||
end
|
||||
|
||||
function run_inline(inline::InlineCode, report::Report, SandBox::Module)
|
||||
#Make a temporary CodeChunk for running code. Collect results and don't wrap
|
||||
chunk = CodeChunk(inline.content, 0, 0, "", Dict(:hold => true, :wrap => false))
|
||||
options = merge(rcParams[:chunk_defaults], chunk.options)
|
||||
merge!(chunk.options, options)
|
||||
|
||||
chunks = eval_chunk(chunk, report, SandBox)
|
||||
contains(report.formatdict[:doctype], "2html") && (chunks = embed_figures(chunks, report.cwd))
|
||||
|
||||
output = chunks[1].output
|
||||
startswith(output, "\n") && (output = replace(output, "\n", "", 1))
|
||||
inline.output = output
|
||||
inline.rich_output = chunks[1].rich_output
|
||||
inline.figures = chunks[1].figures
|
||||
return inline
|
||||
end
|
||||
|
||||
function reset_report(report::Report)
|
||||
report.cur_result = ""
|
||||
report.figures = AbstractString[]
|
||||
|
@ -231,8 +254,9 @@ end
|
|||
#Parse chunk input to array of expressions
|
||||
function parse_input(input::AbstractString)
|
||||
parsed = Tuple{AbstractString, Any}[]
|
||||
input = lstrip(input)
|
||||
n = length(input)
|
||||
pos = 2 #The first character is extra line end
|
||||
pos = 1 #The first character is extra line end
|
||||
while pos ≤ n
|
||||
oldpos = pos
|
||||
code, pos = parse(input, pos)
|
||||
|
@ -243,7 +267,7 @@ end
|
|||
|
||||
|
||||
function eval_chunk(chunk::CodeChunk, report::Report, SandBox::Module)
|
||||
info("Weaving chunk $(chunk.number) from line $(chunk.start_line)")
|
||||
|
||||
|
||||
if !chunk.options[:eval]
|
||||
chunk.output = ""
|
||||
|
|
|
@ -80,14 +80,12 @@ function convert_doc(doc::WeaveDoc, format::NotebookOutput)
|
|||
cells = []
|
||||
ex_count = 1
|
||||
|
||||
doc.chunks[3].content
|
||||
|
||||
for chunk in doc.chunks
|
||||
if typeof(chunk) == Weave.DocChunk
|
||||
if isa(chunk, DocChunk)
|
||||
push!(cells,
|
||||
Dict("cell_type" => "markdown",
|
||||
"metadata" => Dict(),
|
||||
"source" => [strip(chunk.content)])
|
||||
"source" => [strip(join([repr(c) for c in chunk.content], ""))])
|
||||
)
|
||||
else
|
||||
push!(cells,
|
||||
|
@ -111,8 +109,8 @@ end
|
|||
function convert_doc(doc::WeaveDoc, format::MarkdownOutput)
|
||||
output = ""
|
||||
for chunk in doc.chunks
|
||||
if typeof(chunk) == Weave.DocChunk
|
||||
output *= chunk.content
|
||||
if isa(chunk, DocChunk)
|
||||
output *= join([repr(c) for c in chunk.content], "")
|
||||
else
|
||||
output *= "\n" * "```julia"
|
||||
isempty(chunk.optionstring) || (output *= ";" * chunk.optionstring)
|
||||
|
@ -128,8 +126,8 @@ end
|
|||
function convert_doc(doc::WeaveDoc, format::NowebOutput)
|
||||
output = ""
|
||||
for chunk in doc.chunks
|
||||
if typeof(chunk) == Weave.DocChunk
|
||||
output *= chunk.content
|
||||
if isa(chunk, DocChunk)
|
||||
output *= join([repr(c) for c in chunk.content], "")
|
||||
else
|
||||
output *= "\n" * "<<"
|
||||
isempty(chunk.optionstring) || (output *= strip(chunk.optionstring))
|
||||
|
@ -147,7 +145,8 @@ function convert_doc(doc::WeaveDoc, format::ScriptOutput)
|
|||
output = ""
|
||||
for chunk in doc.chunks
|
||||
if typeof(chunk) == Weave.DocChunk
|
||||
output *= join(["#' " * s for s in split(chunk.content, "\n")], "\n")
|
||||
content = join([repr(c) for c in chunk.content], "")
|
||||
output *= join(["#' " * s for s in split(content, "\n")], "\n")
|
||||
else
|
||||
output *= "\n#+ "
|
||||
isempty(chunk.optionstring) || (output *= strip(chunk.optionstring))
|
||||
|
@ -158,3 +157,11 @@ function convert_doc(doc::WeaveDoc, format::ScriptOutput)
|
|||
|
||||
return output
|
||||
end
|
||||
|
||||
function Base.repr(c::InlineText)
|
||||
return c.content
|
||||
end
|
||||
|
||||
function Base.repr(c::InlineCode)
|
||||
return "`j $(c.content)`"
|
||||
end
|
|
@ -20,7 +20,19 @@ Weave.weave("documents/chunk_cache.noweb", plotlib=nothing, cache=:user);
|
|||
cached_result = readstring(out)
|
||||
@test result == cached_result
|
||||
|
||||
if VERSION.minor == 3
|
||||
# cache = :all
|
||||
isdir("documents/cache") && rm("documents/cache", recursive = true)
|
||||
out = "documents/chunk_cache.md"
|
||||
Weave.weave("documents/chunk_cache.noweb", cache=:all);
|
||||
result = readstring(out)
|
||||
rm(out)
|
||||
Weave.weave("documents/chunk_cache.noweb", cache=:all);
|
||||
cached_result = readstring(out)
|
||||
@test result == cached_result
|
||||
|
||||
|
||||
|
||||
if VERSION.minor == 5
|
||||
using Gadfly
|
||||
isdir("documents/cache") && rm("documents/cache", recursive = true)
|
||||
#Caching with Gadfly
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
y= [2, 5, 12]
|
||||
@
|
||||
|
||||
Some text `j print(10)` and continues with another inline code `j y`.
|
||||
|
||||
<<term=true; cache=true>>=
|
||||
y= [2, 5, 12]
|
||||
|
@ -26,6 +27,7 @@ y = 1:5
|
|||
println(y)
|
||||
@
|
||||
|
||||
`j y`
|
||||
|
||||
<<cache=true>>=
|
||||
y = 1:5
|
||||
|
|
|
@ -80,4 +80,4 @@ h_ref = """
|
|||
and some text
|
||||
|
||||
"""
|
||||
@test htext.content == h_ref
|
||||
@test htext.content[1].content == h_ref
|
||||
|
|
Loading…
Reference in New Issue