Merge pull request #312 from JunoLab/avi/format

format things
pull/315/head
Shuhei Kadowaki 2020-05-09 01:10:44 +09:00 committed by GitHub
commit 0c59e92fd7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 1372 additions and 1087 deletions

View File

@ -4,8 +4,14 @@ using Mustache
using Requires
function __init__()
@require Plots="91a5bcdd-55d7-5caf-9e0b-520d859cae80" Base.include(Main, joinpath(@__DIR__, "plots.jl"))
@require Gadfly="c91e804a-d5a3-530f-b6f0-dfbca275c004" Base.include(Main, joinpath(@__DIR__, "gadfly.jl"))
@require Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" Base.include(
Main,
joinpath(@__DIR__, "plots.jl"),
)
@require Gadfly = "c91e804a-d5a3-530f-b6f0-dfbca275c004" Base.include(
Main,
joinpath(@__DIR__, "gadfly.jl"),
)
end
"""
@ -14,12 +20,11 @@ end
List supported output formats
"""
function list_out_formats()
for format = keys(formats)
println(string(format,": ", formats[format].description))
for format in keys(formats)
println(string(format, ": ", formats[format].description))
end
end
"""
tangle(source::AbstractString; kwargs...)
@ -36,8 +41,8 @@ Tangle source code from input document to .jl file.
function tangle(
source::AbstractString;
out_path::Union{Symbol,AbstractString} = :doc,
informat::Union{Symbol,AbstractString} = :auto
)
informat::Union{Symbol,AbstractString} = :auto,
)
doc = read_doc(source, informat)
doc.cwd = get_cwd(doc, out_path)
@ -47,15 +52,14 @@ function tangle(
for chunk in doc.chunks
if typeof(chunk) == CodeChunk
options = merge(doc.chunk_defaults, chunk.options)
options[:tangle] && write(io, chunk.content*"\n")
options[:tangle] && write(io, chunk.content * "\n")
end
end
end
doc.cwd == pwd() && (outname = basename(outname))
doc.cwd == pwd() && (outname = basename(outname))
@info("Writing to file $outname")
end
"""
weave(source::AbstractString; kwargs...)
@ -107,7 +111,7 @@ function weave(
css::Union{Nothing,AbstractString} = nothing,
pandoc_options::Vector{<:AbstractString} = String[],
latex_cmd::AbstractString = "xelatex",
latex_keep_unicode::Bool = false
latex_keep_unicode::Bool = false,
)
doc = read_doc(source, informat)
doctype == :auto && (doctype = detect_doctype(doc.source))
@ -115,25 +119,57 @@ function weave(
# Read args from document header, overrides command line args
if haskey(doc.header, "options")
(doctype, informat, out_path, args, mod, fig_path, fig_ext,
cache_path, cache, throw_errors, template, highlight_theme, css,
pandoc_options, latex_cmd) = header_args(doc, out_path, mod,
fig_ext, fig_path,
cache_path, cache, throw_errors,
template, highlight_theme, css,
pandoc_options, latex_cmd)
(
doctype,
informat,
out_path,
args,
mod,
fig_path,
fig_ext,
cache_path,
cache,
throw_errors,
template,
highlight_theme,
css,
pandoc_options,
latex_cmd,
) = header_args(
doc,
out_path,
mod,
fig_ext,
fig_path,
cache_path,
cache,
throw_errors,
template,
highlight_theme,
css,
pandoc_options,
latex_cmd,
)
end
template != nothing && (doc.template = template)
highlight_theme != nothing && (doc.highlight_theme = highlight_theme)
#theme != nothing && (doc.theme = theme) #Reserved for themes
# theme != nothing && (doc.theme = theme) # Reserved for themes
css != nothing && (doc.css = css)
doc = run(doc, doctype = doctype,
mod = mod,
out_path=out_path, args = args,
fig_path = fig_path, fig_ext = fig_ext, cache_path = cache_path, cache=cache,
throw_errors = throw_errors,latex_keep_unicode=latex_keep_unicode)
doc = run(
doc,
doctype = doctype,
mod = mod,
out_path = out_path,
args = args,
fig_path = fig_path,
fig_ext = fig_ext,
cache_path = cache_path,
cache = cache,
throw_errors = throw_errors,
latex_keep_unicode = latex_keep_unicode,
)
formatted = format(doc)
outname = get_outname(out_path, doc)
@ -142,7 +178,7 @@ function weave(
write(io, formatted)
end
#Special for that need external programs
# Special for that need external programs
if doc.doctype == "pandoc2html"
mdname = outname
outname = get_outname(out_path, doc, ext = "html")
@ -231,7 +267,10 @@ function include_weave(
doc = read_doc(source, informat)
cd(doc.path)
try
code = join([x.content for x in filter(x -> isa(x, Weave.CodeChunk), doc.chunks)], "\n")
code = join(
[x.content for x in filter(x -> isa(x, Weave.CodeChunk), doc.chunks)],
"\n",
)
include_string(m, code)
catch e
throw(e)
@ -240,17 +279,19 @@ function include_weave(
end
end
include_weave(source, informat=:auto) = include_weave(Main, source, informat)
include_weave(source, informat = :auto) = include_weave(Main, source, informat)
#Hooks to run before and after chunks, this is form IJulia,
#but note that Weave hooks take the chunk as input
# Hooks to run before and after chunks, this is form IJulia,
# but note that Weave hooks take the chunk as input
const preexecute_hooks = Function[]
push_preexecute_hook(f::Function) = push!(preexecute_hooks, f)
pop_preexecute_hook(f::Function) = splice!(preexecute_hooks, findfirst(x -> x == f, preexecute_hooks))
pop_preexecute_hook(f::Function) =
splice!(preexecute_hooks, findfirst(x -> x == f, preexecute_hooks))
const postexecute_hooks = Function[]
push_postexecute_hook(f::Function) = push!(postexecute_hooks, f)
pop_postexecute_hook(f::Function) = splice!(postexecute_hooks, findfirst(x -> x == f, postexecute_hooks))
pop_postexecute_hook(f::Function) =
splice!(postexecute_hooks, findfirst(x -> x == f, postexecute_hooks))
include("chunks.jl")
include("config.jl")
@ -264,8 +305,14 @@ include("format.jl")
include("pandoc.jl")
include("writers.jl")
export weave,
list_out_formats,
tangle,
convert_doc,
notebook,
set_chunk_defaults,
get_chunk_defaults,
restore_chunk_defaults,
include_weave
export weave, list_out_formats, tangle, convert_doc, notebook,
set_chunk_defaults, get_chunk_defaults, restore_chunk_defaults,
include_weave
end

View File

@ -1,8 +1,23 @@
#module Markdown2HTML
# module Markdown2HTML
# Markdown to HTML writer, Modified from Julia Base.Markdown html writer
using Markdown: MD, Header, Code, Paragraph, BlockQuote, Footnote, Table,
Admonition, List, HorizontalRule, Bold, Italic, Image, Link, LineBreak,
LaTeX, isordered
using Markdown:
MD,
Header,
Code,
Paragraph,
BlockQuote,
Footnote,
Table,
Admonition,
List,
HorizontalRule,
Bold,
Italic,
Image,
Link,
LineBreak,
LaTeX,
isordered
function tohtml(io::IO, m::MIME"text/html", x)
show(io, m, x)
@ -22,8 +37,6 @@ function tohtml(m::MIME"image/svg+xml", img)
show(io, m, img)
end
# AbstractDisplay infrastructure
function bestmime(val)
@ -35,7 +48,6 @@ end
tohtml(io::IO, x) = tohtml(io, bestmime(x), x)
# Utils
function withtag(f, io::IO, tag, attrs...)
@ -56,10 +68,13 @@ end
tag(io::IO, tag, attrs...) = withtag(nothing, io, tag, attrs...)
const _htmlescape_chars = Dict('<'=>"&lt;", '>'=>"&gt;",
'"'=>"&quot;", '&'=>"&amp;",
# ' '=>"&nbsp;",
)
const _htmlescape_chars = Dict(
'<' => "&lt;",
'>' => "&gt;",
'"' => "&quot;",
'&' => "&amp;",
# ' '=>"&nbsp;",
)
for ch in "'`!\$%()=+{}[]"
_htmlescape_chars[ch] = "&#$(Int(ch));"
end
@ -93,7 +108,7 @@ end
html(io::IO, md::MD) = html(io, md.content)
function html(io::IO, header::Header{l}) where l
function html(io::IO, header::Header{l}) where {l}
withtag(io, "h$l") do
htmlinline(io, header.text)
end
@ -141,7 +156,7 @@ function html(io::IO, md::Admonition)
end
function html(io::IO, md::List)
maybe_attr = md.ordered > 1 ? Any[:start => string(md.ordered)] : []
maybe_attr = md.ordered > 1 ? Any[:start=>string(md.ordered)] : []
withtag(io, isordered(md) ? :ol : :ul, maybe_attr...) do
for item in md.items
println(io)
@ -220,10 +235,9 @@ function htmlinline(io::IO, md::Italic)
end
function htmlinline(io::IO, md::Image)
tag(io, :img, :src=>md.url, :alt=>md.alt)
tag(io, :img, :src => md.url, :alt => md.alt)
end
function htmlinline(io::IO, f::Footnote)
withtag(io, :a, :href => "#footnote-$(f.id)", :class => "footnote") do
print(io, "[", f.id, "]")
@ -231,7 +245,7 @@ function htmlinline(io::IO, f::Footnote)
end
function htmlinline(io::IO, link::Link)
withtag(io, :a, :href=>link.url) do
withtag(io, :a, :href => link.url) do
htmlinline(io, link.text)
end
end
@ -250,4 +264,4 @@ htmlinline(io::IO, x) = tohtml(io, x)
html(md) = sprint(html, md)
#end
# end

View File

@ -7,23 +7,25 @@ function __init__()
# NOTE:
# overwriting `Markdown.latex` function should be done here in order to allow
# incremental precompilations
Markdown.eval(quote
function latex(io::IO, tex::Markdown.LaTeX)
math_envs = ["align", "equation", "eqnarray"]
use_dollars = !any([occursin("\\begin{$me", tex.formula) for me in math_envs])
use_dollars && write(io, "\\[")
write(io, string("\n", tex.formula, "\n"))
use_dollars && write(io, "\\]\n")
end
end)
Markdown.eval(
quote
function latex(io::IO, tex::Markdown.LaTeX)
math_envs = ["align", "equation", "eqnarray"]
use_dollars =
!any([occursin("\\begin{$me", tex.formula) for me in math_envs])
use_dollars && write(io, "\\[")
write(io, string("\n", tex.formula, "\n"))
use_dollars && write(io, "\\]\n")
end
end,
)
end
mutable struct Comment
text::String
end
@breaking true ->
function dollarmath(stream::IO, block::MD)
@breaking true -> function dollarmath(stream::IO, block::MD)
withstream(stream) do
str = Markdown.startswith(stream, r"^\$\$$"m)
isempty(str) && return false
@ -41,20 +43,19 @@ function dollarmath(stream::IO, block::MD)
else
seek(stream, line_start)
end
write(buffer, readline(stream, keep=true))
write(buffer, readline(stream, keep = true))
end
return false
end
end
@breaking true ->
function topcomment(stream::IO, block::MD)
@breaking true -> function topcomment(stream::IO, block::MD)
buffer = IOBuffer()
withstream(stream) do
str = Markdown.startswith(stream, r"^<!--")
isempty(str) && return false
while !eof(stream)
line = readline(stream, keep=true)
line = readline(stream, keep = true)
write(buffer, line)
if occursin(r"-->$", line)
s = replace(String(take!(buffer)) |> chomp, r"-->$" => "")
@ -66,8 +67,7 @@ function topcomment(stream::IO, block::MD)
end
end
@trigger '<' ->
function comment(stream::IO, md::MD)
@trigger '<' -> function comment(stream::IO, md::MD)
withstream(stream) do
Markdown.startswith(stream, "<!--") || return
text = Markdown.readuntil(stream, "-->")

View File

@ -1,9 +1,9 @@
#Serialization is imported only if cache is used
# Serialization is imported only if cache is used
function write_cache(doc::WeaveDoc, cache_path)
cache_dir = joinpath(doc.cwd, cache_path)
isdir(cache_dir) || mkpath(cache_dir)
open(joinpath(cache_dir, doc.basename * ".cache"),"w") do io
isdir(cache_dir) || mkpath(cache_dir)
open(joinpath(cache_dir, doc.basename * ".cache"), "w") do io
Serialization.serialize(io, doc)
end
return nothing
@ -11,47 +11,48 @@ end
function read_cache(doc::WeaveDoc, cache_path)
name = joinpath(doc.cwd, cache_path, doc.basename * ".cache")
isfile(name) || return nothing
open(name,"r") do io
isfile(name) || return nothing
open(name, "r") do io
doc = Serialization.deserialize(io)
end
return doc
end
function restore_chunk(chunk::CodeChunk, cached)
chunks = filter(x -> x.number == chunk.number &&
string(typeof(x)) == "Weave.CodeChunk", cached.chunks)
chunks = filter(
x -> x.number == chunk.number && string(typeof(x)) == "Weave.CodeChunk",
cached.chunks,
)
#Chunk types, don't match after loading. Fix by constructing chunks
#from loaded content
# Chunk types, don't match after loading. Fix by constructing chunks
# from loaded content
new_chunks = Any[]
for c in chunks
newc = CodeChunk(c.content, c.number, c.start_line, c.optionstring, c.options)
newc.result_no = c.result_no
newc.figures = c.figures
newc.result = c.result
newc.output = c.output
newc.rich_output = c.rich_output
push!(new_chunks, newc)
newc = CodeChunk(c.content, c.number, c.start_line, c.optionstring, c.options)
newc.result_no = c.result_no
newc.figures = c.figures
newc.result = c.result
newc.output = c.output
newc.rich_output = c.rich_output
push!(new_chunks, newc)
end
return new_chunks
end
#Restore inline code
# 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)
# 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
# Collect cached code
c_inline = filter(x -> isa(x, InlineCode), c_chunk.content)
isempty(c_inline) && return chunk
#Restore cached results for Inline code
# Restore cached results for Inline code
n = length(chunk.content)
for i in 1:n
for i = 1:n
if isa(chunk.content[i], InlineCode)
ci = filter(x -> x.number == chunk.content[i].number, c_inline)
isempty(ci) && continue

View File

@ -9,11 +9,11 @@ mutable struct WeaveDoc
path::AbstractString
chunks::Array{WeaveChunk}
cwd::AbstractString
format
format::Any
doctype::AbstractString
header_script::String
header::Dict
template::Union{AbstractString, Mustache.MustacheTokens}
template::Union{AbstractString,Mustache.MustacheTokens}
css::AbstractString
highlight_theme::Type{<:Highlights.AbstractTheme}
fig_path::AbstractString
@ -21,8 +21,22 @@ mutable struct WeaveDoc
function WeaveDoc(source, chunks, header)
path, fname = splitdir(abspath(source))
basename = splitext(fname)[1]
new(source, basename, path, chunks, "", nothing, "", "", header,
"", "", Highlights.Themes.DefaultTheme, "", deepcopy(rcParams[:chunk_defaults]))
new(
source,
basename,
path,
chunks,
"",
nothing,
"",
"",
header,
"",
"",
Highlights.Themes.DefaultTheme,
"",
deepcopy(rcParams[:chunk_defaults]),
)
end
end
@ -40,13 +54,24 @@ mutable struct CodeChunk <: WeaveChunk
result_no::Int
start_line::Int
optionstring::AbstractString
options::Dict{Symbol, Any}
options::Dict{Symbol,Any}
output::AbstractString
rich_output::AbstractString
figures::Array{AbstractString}
result::Array{ChunkOutput}
function CodeChunk(content, number, start_line, optionstring, options)
new(rstrip(content) * "\n", number, 0, start_line, optionstring, options, "","", AbstractString[], ChunkOutput[])
new(
rstrip(content) * "\n",
number,
0,
start_line,
optionstring,
options,
"",
"",
AbstractString[],
ChunkOutput[],
)
end
end
@ -54,7 +79,12 @@ mutable struct DocChunk <: WeaveChunk
content::Array{Inline}
number::Int
start_line::Int
function DocChunk(text::AbstractString, number::Int, start_line::Int, inline_regex = nothing)
function DocChunk(
text::AbstractString,
number::Int,
start_line::Int,
inline_regex = nothing,
)
chunks = parse_inline(text, inline_regex)
new(chunks, number, start_line)
end

View File

@ -1,43 +1,42 @@
import Mustache
#Default options
const defaultParams =
Dict{Symbol,Any}(:storeresults => false,
:doc_number => 0,
:chunk_defaults =>
Dict{Symbol,Any}(
:echo=> true,
:results=> "markup",
:hold => false,
:fig=> true,
:include=> true,
:eval => true,
:tangle => true,
:cache => false,
:fig_cap=> nothing,
#Size in inches
:fig_width => 6,
:fig_height => 4,
:fig_path=> "figures",
:dpi => 96,
:term=> false,
:display => false,
:prompt => "\njulia> ",
:label=> nothing,
:wrap=> true,
:line_width => 75,
:engine=> "julia",
#:option_AbstractString=> "",
#Defined in formats
:fig_ext => nothing,
:fig_pos=> nothing,
:fig_env=> nothing,
:out_width=> nothing,
:out_height=> nothing,
:skip=>false
)
)
#This one can be changed at runtime, initially a copy of defaults
# Default options
const defaultParams = Dict{Symbol,Any}(
:storeresults => false,
:doc_number => 0,
:chunk_defaults => Dict{Symbol,Any}(
:echo => true,
:results => "markup",
:hold => false,
:fig => true,
:include => true,
:eval => true,
:tangle => true,
:cache => false,
:fig_cap => nothing,
# Size in inches
:fig_width => 6,
:fig_height => 4,
:fig_path => "figures",
:dpi => 96,
:term => false,
:display => false,
:prompt => "\njulia> ",
:label => nothing,
:wrap => true,
:line_width => 75,
:engine => "julia",
# :option_AbstractString=> "",
# Defined in formats
:fig_ext => nothing,
:fig_pos => nothing,
:fig_env => nothing,
:out_width => nothing,
:out_height => nothing,
:skip => false,
),
)
# This one can be changed at runtime, initially a copy of defaults
const rcParams = deepcopy(defaultParams)
"""
@ -51,9 +50,9 @@ E.g.: set default `dpi` to `200` and `fig_width` to `8`
julia> set_chunk_defaults(Dict{Symbol, Any}(:dpi => 200, fig_width => 8))
```
"""
function set_chunk_defaults(opts::Dict{Symbol, Any})
merge!(rcParams[:chunk_defaults], opts)
return nothing
function set_chunk_defaults(opts::Dict{Symbol,Any})
merge!(rcParams[:chunk_defaults], opts)
return nothing
end
"""
@ -62,7 +61,7 @@ end
Get default options used for code chunks.
"""
function get_chunk_defaults()
return(rcParams[:chunk_defaults])
return (rcParams[:chunk_defaults])
end
"""
@ -71,8 +70,8 @@ end
Restore Weave.jl default chunk options.
"""
function restore_chunk_defaults()
rcParams[:chunk_defaults] = defaultParams[:chunk_defaults]
return nothing
rcParams[:chunk_defaults] = defaultParams[:chunk_defaults]
return nothing
end
"""Combine format specific and common options from document header"""
@ -90,17 +89,28 @@ function combine_args(args, doctype)
common
end
getvalue(d::Dict, key , default) = haskey(d, key) ? d[key] : default
getvalue(d::Dict, key, default) = haskey(d, key) ? d[key] : default
"""
header_args(doc::WeaveDoc)
Get weave arguments from document header.
"""
function header_args(doc::WeaveDoc, out_path, mod, fig_ext, fig_path,
cache_path, cache, throw_errors,template,
highlight_theme, css,
pandoc_options, latex_cmd)
function header_args(
doc::WeaveDoc,
out_path,
mod,
fig_ext,
fig_path,
cache_path,
cache,
throw_errors,
template,
highlight_theme,
css,
pandoc_options,
latex_cmd,
)
args = getvalue(doc.header, "options", Dict())
doctype = getvalue(args, "doctype", doc.doctype)
args = combine_args(args, doctype)
@ -123,9 +133,23 @@ function header_args(doc::WeaveDoc, out_path, mod, fig_ext, fig_path,
pandoc_options = getvalue(args, "pandoc_options", pandoc_options)
latex_cmd = getvalue(args, "latex_cmd", latex_cmd)
return (doctype, informat, out_path, args, mod, fig_path, fig_ext,
cache_path, cache, throw_errors, template, highlight_theme, css,
pandoc_options, latex_cmd)
return (
doctype,
informat,
out_path,
args,
mod,
fig_path,
fig_ext,
cache_path,
cache,
throw_errors,
template,
highlight_theme,
css,
pandoc_options,
latex_cmd,
)
end
"""
@ -136,7 +160,7 @@ Get chunk defaults from header and update.
function header_chunk_defaults!(doc::WeaveDoc)
for key in keys(doc.chunk_defaults)
if haskey(doc.header["options"], String(key))
doc.chunk_defaults[key] = doc.header["options"][String(key)]
doc.chunk_defaults[key] = doc.header["options"][String(key)]
end
end
end

View File

@ -1,51 +1,64 @@
using Markdown
import .WeaveMarkdown
#Contains report global properties
# Contains report global properties
mutable struct Report <: AbstractDisplay
cwd::AbstractString
basename::AbstractString
formatdict::Dict{Symbol,Any}
pending_code::AbstractString
cur_result::AbstractString
rich_output::AbstractString
fignum::Int
figures::Array{AbstractString}
term_state::Symbol
cur_chunk
mimetypes::Array{AbstractString}
first_plot::Bool
header_script::String
throw_errors::Bool
cwd::AbstractString
basename::AbstractString
formatdict::Dict{Symbol,Any}
pending_code::AbstractString
cur_result::AbstractString
rich_output::AbstractString
fignum::Int
figures::Array{AbstractString}
term_state::Symbol
cur_chunk::Any
mimetypes::Array{AbstractString}
first_plot::Bool
header_script::String
throw_errors::Bool
end
function Report(cwd, basename, formatdict, mimetypes, throw_errors)
Report(cwd, basename, formatdict, "", "", "", 1, AbstractString[], :text, nothing,
mimetypes, true, "", throw_errors)
Report(
cwd,
basename,
formatdict,
"",
"",
"",
1,
AbstractString[],
:text,
nothing,
mimetypes,
true,
"",
throw_errors,
)
end
#Default mimetypes in order, can be overridden for some inside `run method` formats
# Default mimetypes in order, can be overridden for some inside `run method` formats
const default_mime_types = ["image/svg+xml", "image/png", "text/html", "text/plain"]
#const default_mime_types = ["image/png", "image/svg+xml", "text/html", "text/plain"]
#From IJulia as a reminder
#const supported_mime_types = [ "text/html", "text/latex", "image/svg+xml", "image/png", "image/jpeg", "text/plain", "text/markdown" ]
# const default_mime_types = ["image/png", "image/svg+xml", "text/html", "text/plain"]
# From IJulia as a reminder
# const supported_mime_types = [ "text/html", "text/latex", "image/svg+xml", "image/png", "image/jpeg", "text/plain", "text/markdown" ]
const mimetype_ext =
Dict(".png" => "image/png",
".jpg" => "image/jpeg",
".jpeg" => "image/jpeg",
".svg" => "image/svg+xml",
".js.svg" => "image/svg+xml",
".pdf" => "application/pdf",
".ps" => "application/postscript",
".tex" => "text/latex"
)
const mimetype_ext = Dict(
".png" => "image/png",
".jpg" => "image/jpeg",
".jpeg" => "image/jpeg",
".svg" => "image/svg+xml",
".js.svg" => "image/svg+xml",
".pdf" => "application/pdf",
".ps" => "application/postscript",
".tex" => "text/latex",
)
function Base.display(report::Report, data)
#Set preferred mimetypes for report based on format
# Set preferred mimetypes for report based on format
fig_ext = report.cur_chunk.options[:fig_ext]
for m in unique([mimetype_ext[fig_ext] ; report.mimetypes])
for m in unique([mimetype_ext[fig_ext]; report.mimetypes])
if Base.invokelatest(showable, m, data)
try
if !istextmime(m)
@ -96,9 +109,9 @@ function Base.display(report::Report, m::MIME"text/html", data::Exception)
end
function Base.show(io, m::MIME"text/html", data::Exception)
println(io ,"<pre class=\"julia-error\">")
println(io, "<pre class=\"julia-error\">")
println(io, Markdown.htmlesc("ERROR: " * sprint(showerror, data)))
println(io ,"</pre>")
println(io, "</pre>")
end
#Catch "rich_output"
@ -113,7 +126,7 @@ function Base.display(report::Report, m::MIME"text/markdown", data)
# Convert to "richer" type of possible
for m in report.mimetypes
if m == "text/html" || m == "text/latex"
display(Markdown.parse(s, flavor=WeaveMarkdown.weavemd))
display(Markdown.parse(s, flavor = WeaveMarkdown.weavemd))
break
elseif m == "text/markdown"
report.rich_output *= "\n" * s
@ -129,18 +142,18 @@ end
"""Add saved figure name to results and return the name"""
function add_figure(report::Report, data, m, ext)
chunk = report.cur_chunk
full_name, rel_name = get_figname(report, chunk, ext = ext)
chunk = report.cur_chunk
full_name, rel_name = get_figname(report, chunk, ext = ext)
open(full_name, "w") do io
if ext == ".pdf"
write(io, repr(m, data))
else
show(io, m, data)
end
end
open(full_name, "w") do io
if ext == ".pdf"
write(io, repr(m, data))
else
show(io, m, data)
end
end
push!(report.figures, rel_name)
report.fignum += 1
return full_name
push!(report.figures, rel_name)
report.fignum += 1
return full_name
end

View File

@ -4,12 +4,11 @@ using Dates
using Markdown
using REPL.REPLCompletions: latex_symbols
function format(doc::WeaveDoc)
formatted = AbstractString[]
docformat = doc.format
#Complete format dictionaries with defaults
# Complete format dictionaries with defaults
formatdict = docformat.formatdict
get!(formatdict, :termstart, formatdict[:codestart])
get!(formatdict, :termend, formatdict[:codeend])
@ -18,7 +17,7 @@ function format(doc::WeaveDoc)
get!(formatdict, :fig_pos, nothing)
get!(formatdict, :fig_env, nothing)
docformat.formatdict[:cwd] = doc.cwd #pass wd to figure formatters
docformat.formatdict[:cwd] = doc.cwd # pass wd to figure formatters
docformat.formatdict[:theme] = doc.highlight_theme
strip_header!(doc)
@ -41,64 +40,84 @@ end
Render formatted document to a template
"""
function render_doc(formatted, doc::WeaveDoc, format)
return formatted
return formatted
end
function highlight(mime::MIME, source::AbstractString, lexer, theme=Highlights.Themes.DefaultTheme)
return sprint( (io, x) -> Highlights.highlight(io, mime, x, lexer, theme), source)
function highlight(
mime::MIME,
source::AbstractString,
lexer,
theme = Highlights.Themes.DefaultTheme,
)
return sprint((io, x) -> Highlights.highlight(io, mime, x, lexer, theme), source)
end
function stylesheet(m::MIME, theme)
return sprint( (io, x) -> Highlights.stylesheet(io, m, x), theme)
return sprint((io, x) -> Highlights.stylesheet(io, m, x), theme)
end
function render_doc(formatted, doc::WeaveDoc, format::JMarkdown2HTML)
css = stylesheet(MIME("text/html"), doc.highlight_theme)
path, wsource = splitdir(abspath(doc.source))
#wversion = string(Pkg.installed("Weave"))
wversion = ""
wtime = string(Date(now()))
css = stylesheet(MIME("text/html"), doc.highlight_theme)
path, wsource = splitdir(abspath(doc.source))
# wversion = string(Pkg.installed("Weave"))
wversion = ""
wtime = string(Date(now()))
if isempty(doc.css)
theme_css = read(joinpath(dirname(@__FILE__), "../templates/skeleton_css.css"), String)
else
theme_css = read(doc.css, String)
end
if isempty(doc.css)
theme_css =
read(joinpath(dirname(@__FILE__), "../templates/skeleton_css.css"), String)
else
theme_css = read(doc.css, String)
end
if isa(doc.template, Mustache.MustacheTokens)
template = doc.template
elseif isempty(doc.template)
template = Mustache.template_from_file(joinpath(dirname(@__FILE__), "../templates/julia_html.tpl"))
else
template = Mustache.template_from_file(doc.template)
end
if isa(doc.template, Mustache.MustacheTokens)
template = doc.template
elseif isempty(doc.template)
template = Mustache.template_from_file(joinpath(
dirname(@__FILE__),
"../templates/julia_html.tpl",
))
else
template = Mustache.template_from_file(doc.template)
end
return Mustache.render(template; themecss = theme_css,
highlightcss = css, body = formatted, header_script = doc.header_script,
source = wsource, wtime = wtime, wversion = wversion,
[Pair(Symbol(k), v) for (k,v) in doc.header]...)
return Mustache.render(
template;
themecss = theme_css,
highlightcss = css,
body = formatted,
header_script = doc.header_script,
source = wsource,
wtime = wtime,
wversion = wversion,
[Pair(Symbol(k), v) for (k, v) in doc.header]...,
)
end
function render_doc(formatted, doc::WeaveDoc, format::JMarkdown2tex)
highlight = stylesheet(MIME("text/latex"), doc.highlight_theme)
path, wsource = splitdir(abspath(doc.source))
#wversion = string(Pkg.installed("Weave"))
wversion = ""
wtime = string(Date(now()))
highlight = stylesheet(MIME("text/latex"), doc.highlight_theme)
path, wsource = splitdir(abspath(doc.source))
# wversion = string(Pkg.installed("Weave"))
wversion = ""
wtime = string(Date(now()))
if isa(doc.template, Mustache.MustacheTokens)
template = doc.template
elseif isempty(doc.template)
template = Mustache.template_from_file(joinpath(
dirname(@__FILE__),
"../templates/julia_tex.tpl",
))
else
template = Mustache.template_from_file(doc.template)
end
if isa(doc.template, Mustache.MustacheTokens)
template = doc.template
elseif isempty(doc.template)
template = Mustache.template_from_file(joinpath(dirname(@__FILE__), "../templates/julia_tex.tpl"))
else
template = Mustache.template_from_file(doc.template)
end
return Mustache.render(template; body = formatted,
highlight = highlight,
[Pair(Symbol(k), v) for (k,v) in doc.header]...)
return Mustache.render(
template;
body = formatted,
highlight = highlight,
[Pair(Symbol(k), v) for (k, v) in doc.header]...,
)
end
strip_header!(doc::WeaveDoc) = strip_header!(doc.chunks[1], doc.doctype)
@ -137,10 +156,10 @@ function format_inline(inline::InlineCode)
isempty(inline.output) || return inline.output
end
function ioformat!(io::IOBuffer, out::IOBuffer, fun=WeaveMarkdown.latex)
function ioformat!(io::IOBuffer, out::IOBuffer, fun = WeaveMarkdown.latex)
text = String(take!(io))
if !isempty(text)
m = Markdown.parse(text, flavor=WeaveMarkdown.weavemd)
m = Markdown.parse(text, flavor = WeaveMarkdown.weavemd)
write(out, string(fun(m)))
end
end
@ -191,15 +210,14 @@ function format_chunk(chunk::DocChunk, formatdict, docformat::JMarkdown2HTML)
end
function format_chunk(chunk::CodeChunk, formatdict, docformat)
#Fill undefined options with format specific defaults
# Fill undefined options with format specific defaults
chunk.options[:out_width] == nothing &&
(chunk.options[:out_width] = formatdict[:out_width])
chunk.options[:fig_pos] == nothing &&
(chunk.options[:fig_pos] = formatdict[:fig_pos])
(chunk.options[:out_width] = formatdict[:out_width])
chunk.options[:fig_pos] == nothing && (chunk.options[:fig_pos] = formatdict[:fig_pos])
#Only use floats if chunk has caption or sets fig_env
# Only use floats if chunk has caption or sets fig_env
if chunk.options[:fig_cap] != nothing && chunk.options[:fig_env] == nothing
(chunk.options[:fig_env] = formatdict[:fig_env])
(chunk.options[:fig_env] = formatdict[:fig_env])
end
if haskey(formatdict, :indent)
@ -222,38 +240,41 @@ function format_chunk(chunk::CodeChunk, formatdict, docformat)
result = format_termchunk(chunk, formatdict, docformat)
else
if chunk.options[:echo]
#Convert to output format and highlight (html, tex...) if needed
result = "$(formatdict[:codestart])$(chunk.content)$(formatdict[:codeend])\n"
else
result = ""
end
if (strip(chunk.output)!= "" || strip(chunk.rich_output) != "") && (chunk.options[:results] != "hidden")
if chunk.options[:results] != "markup" && chunk.options[:results] != "hold"
strip(chunk.output) "" && (result *= "$(chunk.output)\n")
strip(chunk.rich_output) "" && (result *= "$(chunk.rich_output)\n")
if chunk.options[:echo]
# Convert to output format and highlight (html, tex...) if needed
result = "$(formatdict[:codestart])$(chunk.content)$(formatdict[:codeend])\n"
else
if chunk.options[:wrap]
chunk.output = "\n" * wraplines(chunk.output, chunk.options[:line_width])
chunk.output = format_output(chunk.output, docformat)
else
chunk.output = "\n" * rstrip(chunk.output)
chunk.output = format_output(chunk.output, docformat)
end
if haskey(formatdict, :indent)
chunk.output = indent(chunk.output, formatdict[:indent])
end
strip(chunk.output) "" &&
(result *= "$(formatdict[:outputstart])$(chunk.output)\n$(formatdict[:outputend])\n")
strip(chunk.rich_output) "" && (result *= chunk.rich_output * "\n")
result = ""
end
if (strip(chunk.output) != "" || strip(chunk.rich_output) != "") &&
(chunk.options[:results] != "hidden")
if chunk.options[:results] != "markup" && chunk.options[:results] != "hold"
strip(chunk.output) "" && (result *= "$(chunk.output)\n")
strip(chunk.rich_output) "" && (result *= "$(chunk.rich_output)\n")
else
if chunk.options[:wrap]
chunk.output =
"\n" * wraplines(chunk.output, chunk.options[:line_width])
chunk.output = format_output(chunk.output, docformat)
else
chunk.output = "\n" * rstrip(chunk.output)
chunk.output = format_output(chunk.output, docformat)
end
if haskey(formatdict, :indent)
chunk.output = indent(chunk.output, formatdict[:indent])
end
strip(chunk.output) "" && (
result *= "$(formatdict[:outputstart])$(chunk.output)\n$(formatdict[:outputend])\n"
)
strip(chunk.rich_output) "" && (result *= chunk.rich_output * "\n")
end
end
end
end
#Handle figures
# Handle figures
if chunk.options[:fig] && length(chunk.figures) > 0
if chunk.options[:include]
result *= formatfigures(chunk, docformat)
@ -264,34 +285,42 @@ function format_chunk(chunk::CodeChunk, formatdict, docformat)
end
function format_output(result::AbstractString, docformat)
return result
return result
end
function format_output(result::AbstractString, docformat::JMarkdown2HTML)
return Markdown.htmlesc(result)
return Markdown.htmlesc(result)
end
function format_output(result::AbstractString, docformat::JMarkdown2tex)
# Highligts has some extra escaping defined, eg of $, ", ...
result_escaped = sprint( (io, x) -> Highlights.Format.escape(io, MIME("text/latex"), x, charescape=true), result)
docformat.formatdict[:keep_unicode] || return uc2tex(result_escaped, true)
return result_escaped
# Highligts has some extra escaping defined, eg of $, ", ...
result_escaped = sprint(
(io, x) ->
Highlights.Format.escape(io, MIME("text/latex"), x, charescape = true),
result,
)
docformat.formatdict[:keep_unicode] || return uc2tex(result_escaped, true)
return result_escaped
end
function format_code(result::AbstractString, docformat)
return result
return result
end
function format_code(result::AbstractString, docformat::JMarkdown2tex)
highlighted = highlight(MIME("text/latex"), strip(result),
Highlights.Lexers.JuliaLexer, docformat.formatdict[:theme])
docformat.formatdict[:keep_unicode] || return uc2tex(highlighted)
return highlighted
#return "\\begin{minted}[mathescape, fontsize=\\small, xleftmargin=0.5em]{julia}\n$result\n\\end{minted}\n"
highlighted = highlight(
MIME("text/latex"),
strip(result),
Highlights.Lexers.JuliaLexer,
docformat.formatdict[:theme],
)
docformat.formatdict[:keep_unicode] || return uc2tex(highlighted)
return highlighted
# return "\\begin{minted}[mathescape, fontsize=\\small, xleftmargin=0.5em]{julia}\n$result\n\\end{minted}\n"
end
#Convert unicode to tex, escape listings if needed
function uc2tex(s, escape=false)
# Convert unicode to tex, escape listings if needed
function uc2tex(s, escape = false)
for key in keys(latex_symbols)
if escape
s = replace(s, latex_symbols[key] => "(*@\\ensuremath{$(texify(key))}@*)")
@ -320,13 +349,21 @@ function texify(s)
end
function format_code(result::AbstractString, docformat::JMarkdown2HTML)
return highlight(MIME("text/html"), strip(result),
Highlights.Lexers.JuliaLexer, docformat.formatdict[:theme])
return highlight(
MIME("text/html"),
strip(result),
Highlights.Lexers.JuliaLexer,
docformat.formatdict[:theme],
)
end
function format_code(result::AbstractString, docformat::Pandoc2HTML)
return highlight(MIME("text/html"), strip(result),
Highlights.Lexers.JuliaLexer, docformat.formatdict[:theme])
return highlight(
MIME("text/html"),
strip(result),
Highlights.Lexers.JuliaLexer,
docformat.formatdict[:theme],
)
end
function format_termchunk(chunk, formatdict, docformat)
@ -340,8 +377,12 @@ end
function format_termchunk(chunk, formatdict, docformat::JMarkdown2HTML)
if chunk.options[:echo] && chunk.options[:results] != "hidden"
result = highlight(MIME("text/html"), strip(chunk.output),
Highlights.Lexers.JuliaConsoleLexer, docformat.formatdict[:theme])
result = highlight(
MIME("text/html"),
strip(chunk.output),
Highlights.Lexers.JuliaConsoleLexer,
docformat.formatdict[:theme],
)
else
result = ""
end
@ -350,8 +391,12 @@ end
function format_termchunk(chunk, formatdict, docformat::Pandoc2HTML)
if chunk.options[:echo] && chunk.options[:results] != "hidden"
result = highlight(MIME("text/html"), strip(chunk.output),
Highlights.Lexers.JuliaConsoleLexer, docformat.formatdict[:theme])
result = highlight(
MIME("text/html"),
strip(chunk.output),
Highlights.Lexers.JuliaConsoleLexer,
docformat.formatdict[:theme],
)
else
result = ""
end
@ -360,10 +405,13 @@ end
function format_termchunk(chunk, formatdict, docformat::JMarkdown2tex)
if chunk.options[:echo] && chunk.options[:results] != "hidden"
result = highlight(MIME("text/latex"), strip(chunk.output),
Highlights.Lexers.JuliaConsoleLexer,
docformat.formatdict[:theme])
#return "\\begin{minted}[mathescape, fontsize=\\small, xleftmargin=0.5em]{julia}\n$result\n\\end{minted}\n"
result = highlight(
MIME("text/latex"),
strip(chunk.output),
Highlights.Lexers.JuliaConsoleLexer,
docformat.formatdict[:theme],
)
# return "\\begin{minted}[mathescape, fontsize=\\small, xleftmargin=0.5em]{julia}\n$result\n\\end{minted}\n"
else
result = ""
end
@ -371,11 +419,10 @@ function format_termchunk(chunk, formatdict, docformat::JMarkdown2tex)
end
function indent(text, nindent)
return join(map(x->
string(repeat(" ", nindent), x), split(text, "\n")), "\n")
return join(map(x -> string(repeat(" ", nindent), x), split(text, "\n")), "\n")
end
function wraplines(text, line_width=75)
function wraplines(text, line_width = 75)
result = AbstractString[]
lines = split(text, "\n")
for line in lines
@ -389,11 +436,11 @@ function wraplines(text, line_width=75)
return strip(join(result, "\n"))
end
function wrapline(text, line_width=75)
function wrapline(text, line_width = 75)
result = ""
while length(text) > line_width
result*= first(text, line_width) * "\n"
text = chop(text, head=line_width, tail=0)
result *= first(text, line_width) * "\n"
text = chop(text, head = line_width, tail = 0)
end
result *= text
end

View File

@ -5,94 +5,116 @@ struct Tex
formatdict::Dict{Symbol,Any}
end
const tex = Tex("Latex with custom code environments",
Dict{Symbol,Any}(:codestart => "\\begin{juliacode}",
:codeend => "\\end{juliacode}",
:outputstart => "\\begin{juliaout}",
:outputend => "\\end{juliaout}",
:termstart => "\\begin{juliaterm}",
:termend => "\\end{juliaterm}",
:fig_ext => ".pdf",
:extension =>"tex",
:out_width=> "\\linewidth",
:fig_env=> "figure",
:fig_pos => "htpb",
:doctype => "tex",
:mimetypes => ["application/pdf", "image/png", "text/latex", "text/plain"],
:keep_unicode => false,
))
const tex = Tex(
"Latex with custom code environments",
Dict{Symbol,Any}(
:codestart => "\\begin{juliacode}",
:codeend => "\\end{juliacode}",
:outputstart => "\\begin{juliaout}",
:outputend => "\\end{juliaout}",
:termstart => "\\begin{juliaterm}",
:termend => "\\end{juliaterm}",
:fig_ext => ".pdf",
:extension => "tex",
:out_width => "\\linewidth",
:fig_env => "figure",
:fig_pos => "htpb",
:doctype => "tex",
:mimetypes => ["application/pdf", "image/png", "text/latex", "text/plain"],
:keep_unicode => false,
),
)
const texminted = Tex("Latex using minted for highlighting",
Dict{Symbol,Any}(
:codestart => "\\begin{minted}[mathescape, fontsize=\\small, xleftmargin=0.5em]{julia}",
:codeend => "\\end{minted}",
:outputstart => "\\begin{minted}[fontsize=\\small, xleftmargin=0.5em, mathescape, frame = leftline]{text}",
:outputend => "\\end{minted}",
:termstart=> "\\begin{minted}[fontsize=\\footnotesize, xleftmargin=0.5em, mathescape]{jlcon}",
:termend => "\\end{minted}",
:fig_ext => ".pdf",
:extension =>"tex",
:out_width => "\\linewidth",
:fig_env=> "figure",
:fig_pos => "htpb",
:doctype => "texminted",
:mimetypes => ["application/pdf", "image/png", "text/latex", "text/plain"],
:keep_unicode => false,
))
const texminted = Tex(
"Latex using minted for highlighting",
Dict{Symbol,Any}(
:codestart =>
"\\begin{minted}[mathescape, fontsize=\\small, xleftmargin=0.5em]{julia}",
:codeend => "\\end{minted}",
:outputstart =>
"\\begin{minted}[fontsize=\\small, xleftmargin=0.5em, mathescape, frame = leftline]{text}",
:outputend => "\\end{minted}",
:termstart =>
"\\begin{minted}[fontsize=\\footnotesize, xleftmargin=0.5em, mathescape]{jlcon}",
:termend => "\\end{minted}",
:fig_ext => ".pdf",
:extension => "tex",
:out_width => "\\linewidth",
:fig_env => "figure",
:fig_pos => "htpb",
:doctype => "texminted",
:mimetypes => ["application/pdf", "image/png", "text/latex", "text/plain"],
:keep_unicode => false,
),
)
struct Pandoc
description::AbstractString
formatdict::Dict{Symbol,Any}
description::AbstractString
formatdict::Dict{Symbol,Any}
end
const pandoc = Pandoc("Pandoc markdown",
Dict{Symbol,Any}(
:codestart => "~~~~{.julia}",
:codeend=>"~~~~~~~~~~~~~\n\n",
:outputstart=>"~~~~",
:outputend=>"~~~~\n\n",
:fig_ext=>".png",
:out_width=>nothing,
:extension=>"md",
#Prefer png figures for markdown conversion, svg doesn't work with latex
:mimetypes => ["image/png", "image/jpg", "image/svg+xml", "text/markdown", "text/plain"],
:doctype=>"pandoc"
))
const pandoc = Pandoc(
"Pandoc markdown",
Dict{Symbol,Any}(
:codestart => "~~~~{.julia}",
:codeend => "~~~~~~~~~~~~~\n\n",
:outputstart => "~~~~",
:outputend => "~~~~\n\n",
:fig_ext => ".png",
:out_width => nothing,
:extension => "md",
# Prefer png figures for markdown conversion, svg doesn't work with latex
:mimetypes =>
["image/png", "image/jpg", "image/svg+xml", "text/markdown", "text/plain"],
:doctype => "pandoc",
),
)
struct Pandoc2HTML
description::AbstractString
formatdict::Dict{Symbol,Any}
end
const pdoc2html = Pandoc2HTML("Markdown to HTML (requires Pandoc 2)",
Dict{Symbol,Any}(
:codestart => "\n",
:codeend=> "\n",
:outputstart=> "\n",
:outputend=> "\n",
:fig_ext=> ".png",
:extension=> "md",
:mimetypes => ["image/png", "image/svg+xml", "image/jpg",
"text/html", "text/markdown", "text/plain"],
:doctype=> "pandoc2html"))
const pdoc2html = Pandoc2HTML(
"Markdown to HTML (requires Pandoc 2)",
Dict{Symbol,Any}(
:codestart => "\n",
:codeend => "\n",
:outputstart => "\n",
:outputend => "\n",
:fig_ext => ".png",
:extension => "md",
:mimetypes => [
"image/png",
"image/svg+xml",
"image/jpg",
"text/html",
"text/markdown",
"text/plain",
],
:doctype => "pandoc2html",
),
)
struct GitHubMarkdown
description::AbstractString
formatdict::Dict{Symbol,Any}
description::AbstractString
formatdict::Dict{Symbol,Any}
end
const github = GitHubMarkdown("GitHub markdown",
Dict{Symbol,Any}(
:codestart => "````julia",
:codeend=> "````\n\n",
:outputstart=> "````",
:outputend=> "````\n\n",
:fig_ext=> ".png",
:extension=> "md",
:mimetypes => ["image/png", "image/svg+xml", "image/jpg", "text/markdown", "text/plain"],
:doctype=> "github"
))
const github = GitHubMarkdown(
"GitHub markdown",
Dict{Symbol,Any}(
:codestart => "````julia",
:codeend => "````\n\n",
:outputstart => "````",
:outputend => "````\n\n",
:fig_ext => ".png",
:extension => "md",
:mimetypes =>
["image/png", "image/svg+xml", "image/jpg", "text/markdown", "text/plain"],
:doctype => "github",
),
)
"""
Formatter for Hugo: https://gohugo.io/
@ -105,57 +127,79 @@ struct Hugo
uglyURLs::Bool
end
const hugo = Hugo("Hugo markdown (using shortcodes)",
Dict{Symbol,Any}(
:codestart => "````julia",
:codeend => "````\n\n",
:outputstart => "````",
:outputend => "````\n\n",
:fig_ext => ".png",
:extension => "md",
:doctype => "hugo"),
false)
const hugo = Hugo(
"Hugo markdown (using shortcodes)",
Dict{Symbol,Any}(
:codestart => "````julia",
:codeend => "````\n\n",
:outputstart => "````",
:outputend => "````\n\n",
:fig_ext => ".png",
:extension => "md",
:doctype => "hugo",
),
false,
)
#Julia markdown
# Julia markdown
struct JMarkdown2HTML
description::AbstractString
formatdict::Dict{Symbol,Any}
description::AbstractString
formatdict::Dict{Symbol,Any}
end
const md2html = JMarkdown2HTML("Julia markdown to html", Dict{Symbol,Any}(
const md2html = JMarkdown2HTML(
"Julia markdown to html",
Dict{Symbol,Any}(
:codestart => "\n",
:codeend=> "\n",
:outputstart=> "<pre class=\"output\">",
:outputend=> "</pre>\n",
:fig_ext=> ".png",
:mimetypes => ["image/png", "image/jpg", "image/svg+xml",
"text/html", "text/markdown", "text/plain"],
:extension=> "html",
:doctype=> "md2html"))
:codeend => "\n",
:outputstart => "<pre class=\"output\">",
:outputend => "</pre>\n",
:fig_ext => ".png",
:mimetypes => [
"image/png",
"image/jpg",
"image/svg+xml",
"text/html",
"text/markdown",
"text/plain",
],
:extension => "html",
:doctype => "md2html",
),
)
#Julia markdown
# Julia markdown
struct JMarkdown2tex
description::AbstractString
formatdict::Dict{Symbol,Any}
end
const md2tex = JMarkdown2tex("Julia markdown to latex", Dict{Symbol,Any}(
const md2tex = JMarkdown2tex(
"Julia markdown to latex",
Dict{Symbol,Any}(
:codestart => "",
:codeend=> "",
:outputstart=> "\\begin{lstlisting}",
:outputend=> "\\end{lstlisting}\n",
:fig_ext=> ".pdf",
:extension=> "tex",
:codeend => "",
:outputstart => "\\begin{lstlisting}",
:outputend => "\\end{lstlisting}\n",
:fig_ext => ".pdf",
:extension => "tex",
:out_width => "\\linewidth",
:mimetypes => ["application/pdf", "image/png", "image/jpg", "text/latex",
"text/markdown", "text/plain"],
:doctype=> "md2tex",
:keep_unicode=>false))
:mimetypes => [
"application/pdf",
"image/png",
"image/jpg",
"text/latex",
"text/markdown",
"text/plain",
],
:doctype => "md2tex",
:keep_unicode => false,
),
)
struct MultiMarkdown
description::AbstractString
formatdict::Dict{Symbol,Any}
description::AbstractString
formatdict::Dict{Symbol,Any}
end
function formatfigures(chunk, docformat::JMarkdown2HTML)
@ -168,19 +212,18 @@ function formatfigures(chunk, docformat::JMarkdown2HTML)
result = ""
figstring = ""
#Set size
# Set size
attribs = ""
width == nothing || (attribs = "width=\"$width\"")
(attribs != "" && height != nothing ) && (attribs *= ",")
height == nothing || (attribs *= " height=\"$height\" ")
(attribs != "" && height != nothing) && (attribs *= ",")
height == nothing || (attribs *= " height=\"$height\" ")
if caption != nothing
result *= """<figure>\n"""
end
for fig = fignames
figstring *= """<img src="$fig" $attribs />\n"""
for fig in fignames
figstring *= """<img src="$fig" $attribs />\n"""
end
result *= figstring
@ -195,47 +238,51 @@ function formatfigures(chunk, docformat::JMarkdown2HTML)
result *= "</figure>\n"
end
return result
return result
end
const multimarkdown = MultiMarkdown("MultiMarkdown",
Dict{Symbol,Any}(
:codestart => "````julia",
:codeend=> "````\n\n",
:outputstart=> "````",
:outputend=> "````\n\n",
:fig_ext=> ".png",
:extension=> "md",
:doctype=> "github"
))
const multimarkdown = MultiMarkdown(
"MultiMarkdown",
Dict{Symbol,Any}(
:codestart => "````julia",
:codeend => "````\n\n",
:outputstart => "````",
:outputend => "````\n\n",
:fig_ext => ".png",
:extension => "md",
:doctype => "github",
),
)
struct Rest
description::AbstractString
formatdict::Dict{Symbol,Any}
end
const rst = Rest("reStructuredText and Sphinx",
Dict{Symbol,Any}(
:codestart => ".. code-block:: julia\n",
:codeend => "\n\n",
:outputstart => "::\n",
:outputend => "\n\n",
:indent=> 4,
:fig_ext => ".png",
:extension => "rst",
:out_width => "15 cm",
:doctype => "rst"
))
const rst = Rest(
"reStructuredText and Sphinx",
Dict{Symbol,Any}(
:codestart => ".. code-block:: julia\n",
:codeend => "\n\n",
:outputstart => "::\n",
:outputend => "\n\n",
:indent => 4,
:fig_ext => ".png",
:extension => "rst",
:out_width => "15 cm",
:doctype => "rst",
),
)
struct AsciiDoc
description::AbstractString
formatdict::Dict{Symbol,Any}
end
#asciidoc -b html5 -a source-highlighter=pygments ...
const adoc = AsciiDoc("AsciiDoc",
Dict{Symbol,Any}(
# asciidoc -b html5 -a source-highlighter=pygments ...
const adoc = AsciiDoc(
"AsciiDoc",
Dict{Symbol,Any}(
:codestart => "[source,julia]\n--------------------------------------",
:codeend => "--------------------------------------\n\n",
:outputstart => "--------------------------------------",
@ -243,78 +290,76 @@ const adoc = AsciiDoc("AsciiDoc",
:fig_ext => ".png",
:extension => "txt",
:out_width => "600",
:doctype => "asciidoc"
))
:doctype => "asciidoc",
),
)
function md_length_to_latex(def,reference)
if occursin("%",def)
_def = tryparse(Float64,replace(def,"%"=>""))
function md_length_to_latex(def, reference)
if occursin("%", def)
_def = tryparse(Float64, replace(def, "%" => ""))
_def == nothing && return def
perc = round(_def/100,digits=2)
perc = round(_def / 100, digits = 2)
return "$perc$reference"
end
return def
end
function formatfigures(chunk, docformat::Union{Tex,JMarkdown2tex})
fignames = chunk.figures
caption = chunk.options[:fig_cap]
width = chunk.options[:out_width]
height = chunk.options[:out_height]
f_pos = chunk.options[:fig_pos]
f_env = chunk.options[:fig_env]
result = ""
figstring = ""
fignames = chunk.figures
caption = chunk.options[:fig_cap]
width = chunk.options[:out_width]
height = chunk.options[:out_height]
f_pos = chunk.options[:fig_pos]
f_env = chunk.options[:fig_env]
result = ""
figstring = ""
if f_env == nothing && caption != nothing
f_env = "figure"
end
if f_env == nothing && caption != nothing
f_env = "figure"
end
(f_pos == nothing) && (f_pos = "!h")
#Set size
attribs = ""
width == nothing || (attribs = "width=$(md_length_to_latex(width,"\\linewidth"))")
(attribs != "" && height != nothing ) && (attribs *= ",")
height == nothing || (attribs *= "height=$(md_length_to_latex(height,"\\paperheight"))")
(f_pos == nothing) && (f_pos = "!h")
# Set size
attribs = ""
width == nothing || (attribs = "width=$(md_length_to_latex(width,"\\linewidth"))")
(attribs != "" && height != nothing) && (attribs *= ",")
height == nothing || (attribs *= "height=$(md_length_to_latex(height,"\\paperheight"))")
if f_env != nothing
result *= "\\begin{$f_env}"
(f_pos != "") && (result *= "[$f_pos]")
result *= "\n"
end
result *= "\\begin{$f_env}"
(f_pos != "") && (result *= "[$f_pos]")
result *= "\n"
end
for fig = fignames
if splitext(fig)[2] == ".tex" #Tikz figures
figstring *= "\\resizebox{$width}{!}{\\input{$fig}}\n"
else
if isempty(attribs)
figstring *= "\\includegraphics{$fig}\n"
else
figstring *= "\\includegraphics[$attribs]{$fig}\n"
end
end
end
for fig in fignames
if splitext(fig)[2] == ".tex" # Tikz figures
figstring *= "\\resizebox{$width}{!}{\\input{$fig}}\n"
else
if isempty(attribs)
figstring *= "\\includegraphics{$fig}\n"
else
figstring *= "\\includegraphics[$attribs]{$fig}\n"
end
end
end
# Figure environment
if caption != nothing
result *= string("\\center\n",
"$figstring",
"\\caption{$caption}\n")
else
result *= figstring
end
# Figure environment
if caption != nothing
result *= string("\\center\n", "$figstring", "\\caption{$caption}\n")
else
result *= figstring
end
if chunk.options[:label] != nothing && f_env !=nothing
label = chunk.options[:label]
result *= "\\label{fig:$label}\n"
end
if chunk.options[:label] != nothing && f_env != nothing
label = chunk.options[:label]
result *= "\\label{fig:$label}\n"
end
if f_env != nothing
result *= "\\end{$f_env}\n"
end
if f_env != nothing
result *= "\\end{$f_env}\n"
end
return result
return result
end
formatfigures(chunk, docformat::Pandoc2HTML) = formatfigures(chunk, pandoc)
@ -331,16 +376,16 @@ function formatfigures(chunk, docformat::Pandoc)
width = chunk.options[:out_width]
height = chunk.options[:out_height]
#Build figure attibutes
# Build figure attibutes
attribs = String[]
width == nothing || push!(attribs, "width=$width")
width == nothing || push!(attribs, "width=$width")
height == nothing || push!(attribs, "height=$height")
label == nothing || push!(attribs, "#fig:$label")
label == nothing || push!(attribs, "#fig:$label")
attribs = isempty(attribs) ? "" : "{" * join(attribs, " ") * "}"
if caption != nothing
result *= "![$caption]($(fignames[1]))$attribs\n"
for fig = fignames[2:end]
for fig in fignames[2:end]
result *= "![]($fig)$attribs\n"
println("Warning, only the first figure gets a caption\n")
end
@ -362,7 +407,7 @@ function formatfigures(chunk, docformat::GitHubMarkdown)
if caption != nothing
result *= "![$caption]($(fignames[1]))\n"
for fig = fignames[2:end]
for fig in fignames[2:end]
result *= "![]($fig)\n"
println("Warning, only the first figure gets a caption\n")
end
@ -387,7 +432,7 @@ function formatfigures(chunk, docformat::Hugo)
end
"{{< figure src=\"$(joinpath(relpath, fig))\" $(title_spec) >}}"
end
mapreduce(format_shortcode, *, enumerate(chunk.figures), init="")
mapreduce(format_shortcode, *, enumerate(chunk.figures), init = "")
end
function formatfigures(chunk, docformat::MultiMarkdown)
@ -397,25 +442,25 @@ function formatfigures(chunk, docformat::MultiMarkdown)
figstring = ""
if chunk.options[:out_width] == nothing
width = ""
width = ""
else
width = "width=$(chunk.options[:out_width])"
width = "width=$(chunk.options[:out_width])"
end
length(fignames) > 0 || (return "")
if caption != nothing
result *= "![$caption][$(fignames[1])]\n\n"
result *= "[$(fignames[1])]: $(fignames[1]) $width\n"
for fig = fignames[2:end]
result *= "![][$fig]\n\n"
result *= "[$fig]: $fig $width\n"
println("Warning, only the first figure gets a caption\n")
result *= "![$caption][$(fignames[1])]\n\n"
result *= "[$(fignames[1])]: $(fignames[1]) $width\n"
for fig in fignames[2:end]
result *= "![][$fig]\n\n"
result *= "[$fig]: $fig $width\n"
println("Warning, only the first figure gets a caption\n")
end
else
for fig in fignames
result *= "![][$fig]\n\n"
result *= "[$fig]: $fig $width\n"
result *= "![][$fig]\n\n"
result *= "[$fig]: $fig $width\n"
end
end
return result
@ -428,14 +473,16 @@ function formatfigures(chunk, docformat::Rest)
result = ""
figstring = ""
for fig=fignames
for fig in fignames
figstring *= @sprintf(".. image:: %s\n :width: %s\n\n", fig, width)
end
if caption != nothing
result *= string(".. figure:: $(fignames[1])\n",
" :width: $width\n\n",
" $caption\n\n")
result *= string(
".. figure:: $(fignames[1])\n",
" :width: $width\n\n",
" $caption\n\n",
)
else
result *= figstring
return result
@ -449,34 +496,31 @@ function formatfigures(chunk, docformat::AsciiDoc)
result = ""
figstring = ""
for fig=fignames
for fig in fignames
figstring *= @sprintf("image::%s[width=%s]\n", fig, width)
end
if caption != nothing
result *= string("image::$(fignames[1])",
"[width=$width,",
"title=\"$caption\"]")
result *= string("image::$(fignames[1])", "[width=$width,", "title=\"$caption\"]")
else
result *= figstring
return result
end
end
#Add new supported formats here
const formats = Dict{AbstractString, Any}("tex" => tex,
"texminted" => texminted,
"pandoc" => pandoc,
"pandoc2html" => pdoc2html,
"pandoc2pdf" => pandoc,
"md2pdf" => md2tex,
"github" => github,
"hugo" => hugo,
"multimarkdown" => multimarkdown,
"rst" => rst,
"asciidoc" => adoc,
"md2html" => md2html,
"md2tex" => md2tex
)
# Add new supported formats here
const formats = Dict{AbstractString,Any}(
"tex" => tex,
"texminted" => texminted,
"pandoc" => pandoc,
"pandoc2html" => pdoc2html,
"pandoc2pdf" => pandoc,
"md2pdf" => md2tex,
"github" => github,
"hugo" => hugo,
"multimarkdown" => multimarkdown,
"rst" => rst,
"asciidoc" => adoc,
"md2html" => md2html,
"md2tex" => md2tex,
)

View File

@ -21,13 +21,13 @@ function Base.display(report::Weave.Report, m::MIME"image/png", p::Gadfly.Plot)
display(report, MIME("image/svg+xml"), p)
end
#Gadfly doesn't call the default display methods, this catches
#all Gadfly plots
# Gadfly doesn't call the default display methods, this catches
# all Gadfly plots
function Base.display(report::Weave.Report, m::MIME"image/svg+xml", p::Gadfly.Plot)
chunk = report.cur_chunk
w = chunk.options[:fig_width]Gadfly.inch
h = chunk.options[:fig_height]Gadfly.inch
w = chunk.options[:fig_width] * Gadfly.inch
h = chunk.options[:fig_height] * Gadfly.inch
format = chunk.options[:fig_ext]
dpi = chunk.options[:dpi]
@ -41,13 +41,13 @@ function Base.display(report::Weave.Report, m::MIME"image/svg+xml", p::Gadfly.Pl
elseif format == ".js.svg"
Gadfly.draw(Gadfly.SVGJS(full_name, w, h), p)
elseif format == ".png"
Gadfly.draw(Gadfly.PNG(full_name, w, h, dpi=dpi), p)
Gadfly.draw(Gadfly.PNG(full_name, w, h, dpi = dpi), p)
elseif format == ".pdf"
Gadfly.draw(Gadfly.PDF(full_name, w, h), p)
elseif format == ".ps"
Gadfly.draw(Gadfly.PS(full_name, w, h), p)
elseif format == ".tex"
Gadfly.draw(Gadfly.PGF(full_name, w, h, true ), p)
Gadfly.draw(Gadfly.PGF(full_name, w, h, true), p)
else
@warn("Can't save figure. Unsupported format, $format")
end

View File

@ -1,61 +1,65 @@
"""
`pandoc2html(formatted::AbstractString, doc::WeaveDoc)`
Convert output from pandoc markdown to html using Weave.jl template
"""
function pandoc2html(formatted::AbstractString, doc::WeaveDoc, outname::AbstractString, pandoc_options)
weavedir = dirname(@__FILE__)
html_template = joinpath(weavedir, "../templates/pandoc_skeleton.html")
css_template = joinpath(weavedir, "../templates/pandoc_skeleton.css")
css = stylesheet(MIME("text/html"), doc.highlight_theme)
function pandoc2html(
formatted::AbstractString,
doc::WeaveDoc,
outname::AbstractString,
pandoc_options,
)
weavedir = dirname(@__FILE__)
html_template = joinpath(weavedir, "../templates/pandoc_skeleton.html")
css_template = joinpath(weavedir, "../templates/pandoc_skeleton.css")
css = stylesheet(MIME("text/html"), doc.highlight_theme)
path, wsource = splitdir(abspath(doc.source))
#wversion = string(Pkg.installed("Weave"))
wversion = ""
wtime = string(Date(now()))
path, wsource = splitdir(abspath(doc.source))
# wversion = string(Pkg.installed("Weave"))
wversion = ""
wtime = string(Date(now()))
#Header is inserted from displayed plots
header_script = doc.header_script
self_contained = (header_script "") ? [] : "--self-contained"
# Header is inserted from displayed plots
header_script = doc.header_script
self_contained = (header_script "") ? [] : "--self-contained"
if haskey(doc.header, "bibliography")
filt = "--filter"
citeproc = "pandoc-citeproc"
else
filt = []
citeproc = []
end
if haskey(doc.header, "bibliography")
filt = "--filter"
citeproc = "pandoc-citeproc"
else
filt = []
citeproc = []
end
#Change path for pandoc
old_wd = pwd()
cd(doc.cwd)
html =""
outname = basename(outname)
# Change path for pandoc
old_wd = pwd()
cd(doc.cwd)
html = ""
outname = basename(outname)
open("temp.md", "w") do io
println(io, formatted)
end
open("temp.md", "w") do io
println(io, formatted)
end
try
cmd = `pandoc -f markdown+raw_html -s --mathjax=""
$filt $citeproc $pandoc_options
--template $html_template -H $css_template $self_contained
-V wversion=$wversion -V wtime=$wtime -V wsource=$wsource
-V highlightcss=$css
-V headerscript=$header_script
-o $outname`
proc = open(cmd, "r+")
println(proc.in, formatted)
close(proc.in)
proc_output = read(proc.out, String)
cd(old_wd)
catch e
cd(old_wd)
@warn("Error converting document to HTML")
throw(e)
end
try
cmd = `pandoc -f markdown+raw_html -s --mathjax=""
$filt $citeproc $pandoc_options
--template $html_template -H $css_template $self_contained
-V wversion=$wversion -V wtime=$wtime -V wsource=$wsource
-V highlightcss=$css
-V headerscript=$header_script
-o $outname`
proc = open(cmd, "r+")
println(proc.in, formatted)
close(proc.in)
proc_output = read(proc.out, String)
cd(old_wd)
catch e
cd(old_wd)
@warn("Error converting document to HTML")
throw(e)
end
end
"""
@ -63,64 +67,75 @@ end
Convert output from pandoc markdown to pdf using Weave.jl template
"""
function pandoc2pdf(formatted::AbstractString, doc::WeaveDoc, outname::AbstractString, pandoc_options)
weavedir = dirname(@__FILE__)
header_template = joinpath(weavedir, "../templates/pandoc_header.txt")
function pandoc2pdf(
formatted::AbstractString,
doc::WeaveDoc,
outname::AbstractString,
pandoc_options,
)
weavedir = dirname(@__FILE__)
header_template = joinpath(weavedir, "../templates/pandoc_header.txt")
path, wsource = splitdir(abspath(doc.source))
#wversion = string(Pkg.installed("Weave"))
wversion = ""
wtime = Date(now())
outname = basename(outname)
path, wsource = splitdir(abspath(doc.source))
# wversion = string(Pkg.installed("Weave"))
wversion = ""
wtime = Date(now())
outname = basename(outname)
#Change path for pandoc
old_wd = pwd()
cd(doc.cwd)
html =""
# Change path for pandoc
old_wd = pwd()
cd(doc.cwd)
html = ""
if haskey(doc.header, "bibliography")
filt = "--filter"
citeproc = "pandoc-citeproc"
else
filt = []
citeproc = []
end
if haskey(doc.header, "bibliography")
filt = "--filter"
citeproc = "pandoc-citeproc"
else
filt = []
citeproc = []
end
@info("Done executing code. Running xelatex")
try
cmd = `pandoc -f markdown+raw_tex -s --pdf-engine=xelatex --highlight-style=tango
$filt $citeproc $pandoc_options
--include-in-header=$header_template
-V fontsize=12pt -o $outname`
proc = open(cmd, "r+")
println(proc.in, formatted)
close(proc.in)
proc_output = read(proc.out, String)
cd(old_wd)
catch e
cd(old_wd)
@warn("Error converting document to pdf")
throw(e)
end
@info("Done executing code. Running xelatex")
try
cmd = `pandoc -f markdown+raw_tex -s --pdf-engine=xelatex --highlight-style=tango
$filt $citeproc $pandoc_options
--include-in-header=$header_template
-V fontsize=12pt -o $outname`
proc = open(cmd, "r+")
println(proc.in, formatted)
close(proc.in)
proc_output = read(proc.out, String)
cd(old_wd)
catch e
cd(old_wd)
@warn("Error converting document to pdf")
throw(e)
end
end
function run_latex(doc::WeaveDoc, outname, latex_cmd = "xelatex")
old_wd = pwd()
cd(doc.cwd)
xname = basename(outname)
@info("Weaved code to $outname . Running $latex_cmd") # space before '.' added for link to be clickable in Juno terminal
textmp = mktempdir(".")
try
out = read(`$latex_cmd -shell-escape $xname -aux-directory $textmp -include-directory $(doc.cwd)`, String)
out = read(`$latex_cmd -shell-escape $xname -aux-directory $textmp -include-directory $(doc.cwd)`, String)
rm(xname)
rm(textmp, recursive=true)
cd(old_wd)
return true
catch e
@warn("Error converting document to pdf. Try running latex manually")
cd(old_wd)
rm(textmp)
return false
end
old_wd = pwd()
cd(doc.cwd)
xname = basename(outname)
@info("Weaved code to $outname . Running $latex_cmd") # space before '.' added for link to be clickable in Juno terminal
textmp = mktempdir(".")
try
out = read(
`$latex_cmd -shell-escape $xname -aux-directory $textmp -include-directory $(doc.cwd)`,
String,
)
out = read(
`$latex_cmd -shell-escape $xname -aux-directory $textmp -include-directory $(doc.cwd)`,
String,
)
rm(xname)
rm(textmp, recursive = true)
cd(old_wd)
return true
catch e
@warn("Error converting document to pdf. Try running latex manually")
cd(old_wd)
rm(textmp)
return false
end
end

View File

@ -6,64 +6,78 @@ import Weave
"""Pre-execute hooks to set the plot size for the chunk """
function plots_set_size(chunk)
w = chunk.options[:fig_width] * chunk.options[:dpi]
h = chunk.options[:fig_height] * chunk.options[:dpi]
Plots.default(size = (w,h))
return chunk
w = chunk.options[:fig_width] * chunk.options[:dpi]
h = chunk.options[:fig_height] * chunk.options[:dpi]
Plots.default(size = (w, h))
return chunk
end
Weave.push_preexecute_hook(plots_set_size)
#PNG or SVG is not working, output html
function Base.display(report::Weave.Report, m::MIME"image/svg+xml", data::Plots.Plot{Plots.PlotlyBackend})#
#Remove extra spaces from start of line for pandoc
s = repr(MIME("text/html"), data)
splitted = split(s, "\n")
start = split(splitted[1], r"(?=<div)")
#script = lstrip(start[1]) #local
# PNG or SVG is not working, output html
function Base.display(
report::Weave.Report,
m::MIME"image/svg+xml",
data::Plots.Plot{Plots.PlotlyBackend},
)#
# Remove extra spaces from start of line for pandoc
s = repr(MIME("text/html"), data)
splitted = split(s, "\n")
start = split(splitted[1], r"(?=<div)")
# script = lstrip(start[1]) # local
div = lstrip(start[2])
plot = join(map(lstrip, splitted[2:end]), "\n")
div = lstrip(start[2])
plot = join(map(lstrip, splitted[2:end]), "\n")
if report.first_plot
report.header_script *= "<script src=\"https://cdn.plot.ly/plotly-latest.min.js\"></script>"
report.first_plot = false
end
if report.first_plot
report.header_script *= "<script src=\"https://cdn.plot.ly/plotly-latest.min.js\"></script>"
report.first_plot = false
end
report.rich_output *= "\n" * div * "\n" * plot
end
function Base.display(report::Weave.Report, m::MIME"image/png", data::Plots.Plot{Plots.PlotlyBackend})#
display(report, MIME("image/svg+xml"), data)
function Base.display(
report::Weave.Report,
m::MIME"image/png",
data::Plots.Plot{Plots.PlotlyBackend},
)#
display(report, MIME("image/svg+xml"), data)
end
# PNG or SVG is not working, output html
function Base.display(
report::Weave.Report,
m::MIME"image/svg+xml",
plot::Plots.Plot{Plots.PlotlyJSBackend},
)
body = Plots.PlotlyJS.html_body(plot.o.plot)
#PNG or SVG is not working, output html
function Base.display(report::Weave.Report, m::MIME"image/svg+xml", plot::Plots.Plot{Plots.PlotlyJSBackend})
body = Plots.PlotlyJS.html_body(plot.o.plot)
if report.first_plot
report.header_script *= "<script src=\"https://cdn.plot.ly/plotly-latest.min.js\"></script>"
report.first_plot = false
end
if report.first_plot
report.header_script *= "<script src=\"https://cdn.plot.ly/plotly-latest.min.js\"></script>"
report.first_plot = false
end
report.rich_output *= "\n" * body
report.rich_output *= "\n" * body
end
function Base.display(report::Weave.Report, m::MIME"image/png", plot::Plots.Plot{Plots.PlotlyJSBackend})
display(report, MIME("image/svg+xml"), data)
function Base.display(
report::Weave.Report,
m::MIME"image/png",
plot::Plots.Plot{Plots.PlotlyJSBackend},
)
display(report, MIME("image/svg+xml"), data)
end
"""Add saved figure name to results and return the name"""
function add_plots_figure(report::Weave.Report, plot::Plots.Plot, ext)
chunk = report.cur_chunk
full_name, rel_name = Weave.get_figname(report, chunk, ext = ext)
chunk = report.cur_chunk
full_name, rel_name = Weave.get_figname(report, chunk, ext = ext)
Plots.savefig(plot, full_name)
push!(report.figures, rel_name)
report.fignum += 1
return full_name
Plots.savefig(plot, full_name)
push!(report.figures, rel_name)
report.fignum += 1
return full_name
end
function Base.display(report::Weave.Report, m::MIME"application/pdf", plot::Plots.Plot)
@ -80,21 +94,21 @@ end
# write out html to view Animated gif
function Base.display(report::Weave.Report, ::MIME"text/html", agif::Plots.AnimatedGif)
ext = agif.filename[end-2:end]
res = ""
if ext == "gif"
img = stringmime(MIME("image/gif"), read(agif.filename))
res = "<img src=\"data:image/gif;base64,$img\" />"
elseif ext in ("mov", "mp4")
#Uncomment to embed mp4, make global or chunk option?
#img = stringmime(MIME("video/$ext"), read(agif.filename))
#res = "<video controls><source src=\"data:video/$(ext);base64,$img\" type=\"video/$ext\"></video>"
res = "<video controls><source src=\"$(relpath(agif.filename))\" type=\"video/$ext\"></video>"
else
error("Cannot show animation with extension $ext: $agif")
end
ext = agif.filename[end-2:end]
res = ""
if ext == "gif"
img = stringmime(MIME("image/gif"), read(agif.filename))
res = "<img src=\"data:image/gif;base64,$img\" />"
elseif ext in ("mov", "mp4")
# Uncomment to embed mp4, make global or chunk option?
# img = stringmime(MIME("video/$ext"), read(agif.filename))
# res = "<video controls><source src=\"data:video/$(ext);base64,$img\" type=\"video/$ext\"></video>"
res = "<video controls><source src=\"$(relpath(agif.filename))\" type=\"video/$ext\"></video>"
else
error("Cannot show animation with extension $ext: $agif")
end
report.rich_output *= "\n" * res * "\n"
report.rich_output *= "\n" * res * "\n"
end
end

View File

@ -1,6 +1,7 @@
import JSON, YAML
pushopt(options::Dict,expr::Expr) = Base.Meta.isexpr(expr,:(=)) && (options[expr.args[1]] = expr.args[2])
pushopt(options::Dict, expr::Expr) =
Base.Meta.isexpr(expr, :(=)) && (options[expr.args[1]] = expr.args[2])
mutable struct MarkupInput
codestart::Regex
@ -9,47 +10,46 @@ mutable struct MarkupInput
end
mutable struct ScriptInput
doc_line::Regex
doc_start::Regex
opt_line::Regex
opt_start::Regex
inline::Regex
doc_line::Regex
doc_start::Regex
opt_line::Regex
opt_start::Regex
inline::Regex
end
mutable struct NotebookInput
inline
inline::Any
end
const input_formats = Dict{AbstractString, Any}(
"noweb" => MarkupInput(r"^<<(.*?)>>=\s*$",
r"^@\s*$",
r"`j\s+(.*?)`|^!\s(.*)$"m
),
"markdown" => MarkupInput(
r"^[`~]{3,}(?:\{|\{\.|)julia(?:;|)\s*(.*?)(\}|\s*)$",
r"^[`~]{3,}\s*$",
r"`j\s+(.*?)`|^!\s(.*)$"m),
"script" => ScriptInput(
r"(^#'.*)|(^#%%.*)|(^# %%.*)",
r"(^#')|(^#%%)|(^# %%)",
r"(^#\+.*$)|(^#%%\+.*$)|(^# %%\+.*$)",
r"(^#\+)|(^#%%\+)|(^# %%\+)",
r"`j\s+(.*?)`|^!\s(.*)$"m),
"notebook" => NotebookInput(nothing) #Don't parse inline code from notebooks
)
const input_formats = Dict{AbstractString,Any}(
"noweb" => MarkupInput(r"^<<(.*?)>>=\s*$", r"^@\s*$", r"`j\s+(.*?)`|^!\s(.*)$"m),
"markdown" => MarkupInput(
r"^[`~]{3,}(?:\{|\{\.|)julia(?:;|)\s*(.*?)(\}|\s*)$",
r"^[`~]{3,}\s*$",
r"`j\s+(.*?)`|^!\s(.*)$"m,
),
"script" => ScriptInput(
r"(^#'.*)|(^#%%.*)|(^# %%.*)",
r"(^#')|(^#%%)|(^# %%)",
r"(^#\+.*$)|(^#%%\+.*$)|(^# %%\+.*$)",
r"(^#\+)|(^#%%\+)|(^# %%\+)",
r"`j\s+(.*?)`|^!\s(.*)$"m,
),
"notebook" => NotebookInput(nothing), # Don't parse inline code from notebooks
)
"""Detect the input format based on file extension"""
function detect_informat(source::AbstractString)
ext = lowercase(splitext(source)[2])
ext = lowercase(splitext(source)[2])
ext == ".jl" && return "script"
ext == ".jmd" && return "markdown"
ext == ".ipynb" && return "notebook"
return "noweb"
ext == ".jl" && return "script"
ext == ".jmd" && return "markdown"
ext == ".ipynb" && return "notebook"
return "noweb"
end
"""Read and parse input document"""
function read_doc(source::AbstractString, format=:auto)
function read_doc(source::AbstractString, format = :auto)
format == :auto && (format = detect_informat(source))
document = read(source, String)
document = replace(document, "\r\n" => "\n")
@ -61,230 +61,232 @@ function read_doc(source::AbstractString, format=:auto)
end
function parse_header(chunk::CodeChunk)
return Dict()
return Dict()
end
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
m = match(HEADER_REGEX, chunk.content[1].content)
if m !== nothing
header = YAML.load(string(m[:header]))
else
header = Dict()
end
return header
end
function parse_doc(document::AbstractString, format="noweb"::AbstractString)
return parse_doc(document, input_formats[format])
function parse_doc(document::AbstractString, format = "noweb"::AbstractString)
return parse_doc(document, input_formats[format])
end
"""Parse documents with Weave.jl markup"""
function parse_doc(document::AbstractString, format::MarkupInput)
document = replace(document, "\r\n" => "\n")
lines = split(document, "\n")
document = replace(document, "\r\n" => "\n")
lines = split(document, "\n")
codestart = format.codestart
codeend = format.codeend
state = "doc"
codestart = format.codestart
codeend = format.codeend
state = "doc"
docno = 1
codeno = 1
content = ""
start_line = 0
docno = 1
codeno = 1
content = ""
start_line = 0
options = Dict()
optionString = ""
parsed = Any[]
for lineno in 1:length(lines)
line = lines[lineno]
if (m = match(codestart, line)) != nothing && state=="doc"
state = "code"
if m.captures[1] == nothing
optionString = ""
else
optionString=strip(m.captures[1])
end
options = Dict()
optionString = ""
parsed = Any[]
for lineno = 1:length(lines)
line = lines[lineno]
if (m = match(codestart, line)) != nothing && state == "doc"
state = "code"
if m.captures[1] == nothing
optionString = ""
else
optionString = strip(m.captures[1])
end
options = Dict{Symbol,Any}()
if length(optionString) > 0
expr = Meta.parse(optionString)
Base.Meta.isexpr(expr,:(=)) && (options[expr.args[1]] = expr.args[2])
Base.Meta.isexpr(expr,:toplevel) && map(pushopt,fill(options,length(expr.args)),expr.args)
end
haskey(options, :label) && (options[:name] = options[:label])
haskey(options, :name) || (options[:name] = nothing)
options = Dict{Symbol,Any}()
if length(optionString) > 0
expr = Meta.parse(optionString)
Base.Meta.isexpr(expr, :(=)) && (options[expr.args[1]] = expr.args[2])
Base.Meta.isexpr(expr, :toplevel) &&
map(pushopt, fill(options, length(expr.args)), expr.args)
end
haskey(options, :label) && (options[:name] = options[:label])
haskey(options, :name) || (options[:name] = nothing)
if !isempty(strip(content))
if !isempty(strip(content))
chunk = DocChunk(content, docno, start_line, format.inline)
docno += 1
push!(parsed, chunk)
end
content = ""
start_line = lineno
continue
end
if occursin(codeend, line) && state == "code"
chunk = CodeChunk(content, codeno, start_line, optionString, options)
codeno += 1
start_line = lineno
content = ""
state = "doc"
push!(parsed, chunk)
continue
end
if lineno == 1
content *= line
else
content *= "\n" * line
end
end
# Remember the last chunk
if strip(content) != ""
chunk = DocChunk(content, docno, start_line, format.inline)
docno += 1
# chunk = Dict{Symbol,Any}(:type => "doc", :content => content,
# :number => docno, :start_line => start_line)
push!(parsed, chunk)
end
content = ""
start_line = lineno
continue
end
if occursin(codeend, line) && state=="code"
chunk = CodeChunk(content, codeno, start_line, optionString, options)
codeno+=1
start_line = lineno
content = ""
state = "doc"
push!(parsed, chunk)
continue
end
if lineno == 1
content *= line
else
content *= "\n" * line
end
end
#Remember the last chunk
if strip(content) != ""
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)
end
return parsed
return parsed
end
"""Parse .jl scripts with Weave.jl markup"""
function parse_doc(document::AbstractString, format::ScriptInput)
document = replace(document, "\r\n" => "\n")
lines = split(document, "\n")
document = replace(document, "\r\n" => "\n")
lines = split(document, "\n")
doc_line = format.doc_line
doc_start = format.doc_start
opt_line = format.opt_line
opt_start = format.opt_start
doc_line = format.doc_line
doc_start = format.doc_start
opt_line = format.opt_line
opt_start = format.opt_start
read = ""
chunks = []
docno = 1
codeno = 1
content = ""
start_line = 1
options = Dict{Symbol,Any}()
optionString = ""
parsed = Any[]
state = "code"
lineno = 1
n_emptylines = 0
read = ""
chunks = []
docno = 1
codeno = 1
content = ""
start_line = 1
options = Dict{Symbol,Any}()
optionString = ""
parsed = Any[]
state = "code"
lineno = 1
n_emptylines = 0
for lineno = 1:length(lines)
line = lines[lineno]
if (m = match(doc_line, line)) != nothing && (m = match(opt_line, line)) == nothing
line = replace(line, doc_start => "", count = 1)
if startswith(line, " ")
line = replace(line, " " => "", count = 1)
end
if state == "code" && strip(read) != ""
chunk =
CodeChunk("\n" * strip(read), codeno, start_line, optionString, options)
push!(parsed, chunk)
codeno += 1
read = ""
start_line = lineno
end
state = "doc"
elseif (m = match(opt_line, line)) != nothing
start_line = lineno
if state == "code" && strip(read) != ""
chunk =
CodeChunk("\n" * strip(read), codeno, start_line, optionString, options)
push!(parsed, chunk)
read = ""
codeno += 1
end
if state == "doc" && strip(read) != ""
(docno > 1) && (read = "\n" * read) # Add whitespace to doc chunk. Needed for markdown output
chunk = DocChunk(read, docno, start_line)
push!(parsed, chunk)
read = ""
docno += 1
end
optionString = replace(line, opt_start => "", count = 1)
# Get options
options = Dict{Symbol,Any}()
if length(optionString) > 0
expr = Meta.parse(optionString)
Base.Meta.isexpr(expr, :(=)) && (options[expr.args[1]] = expr.args[2])
Base.Meta.isexpr(expr, :toplevel) &&
map(pushopt, fill(options, length(expr.args)), expr.args)
end
haskey(options, :label) && (options[:name] = options[:label])
haskey(options, :name) || (options[:name] = nothing)
for lineno in 1:length(lines)
line = lines[lineno]
if (m = match(doc_line, line)) != nothing && (m = match(opt_line, line)) == nothing
line = replace(line, doc_start => "", count=1)
if startswith(line, " ")
line = replace(line, " " => "", count=1)
end
if state == "code" && strip(read) != ""
chunk = CodeChunk("\n" * strip(read), codeno, start_line, optionString, options)
push!(parsed, chunk)
codeno +=1
read = ""
start_line = lineno
end
state = "doc"
elseif (m = match(opt_line, line)) != nothing
start_line = lineno
if state == "code" && strip(read) !=""
chunk = CodeChunk("\n" * strip(read), codeno, start_line, optionString, options)
push!(parsed, chunk)
read = ""
codeno +=1
end
if state == "doc" && strip(read) != ""
(docno > 1) && (read = "\n" * read) # Add whitespace to doc chunk. Needed for markdown output
chunk = DocChunk(read, docno, start_line)
push!(parsed, chunk)
read = ""
docno += 1
end
state = "code"
continue
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, format.inline)
push!(parsed, chunk)
options = Dict{Symbol,Any}()
start_line = lineno
read = ""
docno += 1
end
read *= line * "\n"
optionString = replace(line, opt_start => "", count=1)
#Get options
options = Dict{Symbol,Any}()
if length(optionString) > 0
expr = Meta.parse(optionString)
Base.Meta.isexpr(expr,:(=)) && (options[expr.args[1]] = expr.args[2])
Base.Meta.isexpr(expr,:toplevel) && map(pushopt,fill(options,length(expr.args)),expr.args)
end
haskey(options, :label) && (options[:name] = options[:label])
haskey(options, :name) || (options[:name] = nothing)
state = "code"
continue
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, format.inline)
push!(parsed, chunk)
options = Dict{Symbol,Any}()
start_line = lineno
read = ""
docno += 1
if strip(line) == ""
n_emptylines += 1
else
n_emptylines = 0
end
end
read *= line * "\n"
if strip(line) == ""
n_emptylines += 1
# Handle the last chunk
if state == "code"
chunk = CodeChunk("\n" * strip(read), codeno, start_line, optionString, options)
push!(parsed, chunk)
else
n_emptylines = 0
chunk = DocChunk(read, docno, start_line, format.inline)
push!(parsed, chunk)
end
end
# Handle the last chunk
if state == "code"
chunk = CodeChunk("\n" * strip(read), codeno, start_line, optionString, options)
push!(parsed, chunk)
else
chunk = DocChunk(read, docno, start_line, format.inline)
push!(parsed, chunk)
end
return parsed
return parsed
end
"""Parse IJUlia notebook"""
function parse_doc(document::String, format::NotebookInput)
document = replace(document, "\r\n" => "\n")
nb = JSON.parse(document)
parsed = Any[]
options = Dict{Symbol,Any}()
opt_string = ""
docno = 1
codeno = 1
document = replace(document, "\r\n" => "\n")
nb = JSON.parse(document)
parsed = Any[]
options = Dict{Symbol,Any}()
opt_string = ""
docno = 1
codeno = 1
for cell in nb["cells"]
srctext = "\n" * join(cell["source"], "")
for cell in nb["cells"]
srctext = "\n" * join(cell["source"], "")
if cell["cell_type"] == "code"
chunk = CodeChunk(rstrip(srctext), codeno, 0, opt_string, options)
push!(parsed, chunk)
codeno += 1
else
chunk = DocChunk(srctext * "\n", docno, 0)
push!(parsed, chunk)
docno +=1
if cell["cell_type"] == "code"
chunk = CodeChunk(rstrip(srctext), codeno, 0, opt_string, options)
push!(parsed, chunk)
codeno += 1
else
chunk = DocChunk(srctext * "\n", docno, 0)
push!(parsed, chunk)
docno += 1
end
end
end
return parsed
return parsed
end
#Use this if regex is undefined
# Use this if regex is undefined
function parse_inline(text, noex)
return Inline[InlineText(text, 1, length(text), 1)]
end
@ -301,7 +303,7 @@ function parse_inline(text::AbstractString, inline_ex::Regex)
for ic in inline_chunks
s = ic.offset
doc = InlineText(text[e:(s-1)], e, s-1, textno)
doc = InlineText(text[e:(s-1)], e, s - 1, textno)
textno += 1
push!(res, doc)
e = s + lastindex(ic.match)

View File

@ -39,9 +39,9 @@ function Base.run(
cache_path::AbstractString = "cache",
cache::Symbol = :off,
throw_errors::Bool = false,
latex_keep_unicode::Bool = false
latex_keep_unicode::Bool = false,
)
#cache :all, :user, :off, :refresh
# cache :all, :user, :off, :refresh
doc.cwd = get_cwd(doc, out_path)
doctype == :auto && (doctype = detect_doctype(doc.source))
@ -62,13 +62,13 @@ function Base.run(
cache == :off || @eval import Serialization
#This is needed for latex and should work on all output formats
# This is needed for latex and should work on all output formats
Sys.iswindows() && (fig_path = replace(fig_path, "\\" => "/"))
doc.fig_path = fig_path
set_rc_params(doc, fig_path, fig_ext)
#New sandbox for each document with args exposed
# New sandbox for each document with args exposed
if mod == :sandbox
sandbox = "WeaveSandBox$(rcParams[:doc_number])"
mod = Core.eval(Main, Meta.parse("module $sandbox\nend"))
@ -79,9 +79,9 @@ function Base.run(
rcParams[:doc_number] += 1
if haskey(doc.format.formatdict, :mimetypes)
mimetypes = doc.format.formatdict[:mimetypes]
mimetypes = doc.format.formatdict[:mimetypes]
else
mimetypes = default_mime_types
mimetypes = default_mime_types
end
report = Report(doc.cwd, doc.basename, doc.format.formatdict, mimetypes, throw_errors)
@ -105,9 +105,9 @@ function Base.run(
merge!(chunk.options, options)
end
restore = (cache ==:user && typeof(chunk) == CodeChunk && chunk.options[:cache])
restore = (cache == :user && typeof(chunk) == CodeChunk && chunk.options[:cache])
if cached != nothing && (cache == :all || restore)
if cached != nothing && (cache == :all || restore)
result_chunks = restore_chunk(chunk, cached)
else
result_chunks = run_chunk(chunk, doc, report, mod)
@ -120,7 +120,7 @@ function Base.run(
popdisplay(report)
#Clear variables from used sandbox
# Clear variables from used sandbox
mod == :sandbox && clear_sandbox(SandBox)
doc.chunks = executed
@ -142,16 +142,16 @@ function detect_doctype(path::AbstractString)
match(r"^\.(jl|.?md|ipynb)", ext) !== nothing && return "md2html"
ext == ".rst" && return "rst"
ext == ".tex" && return "texminted"
ext == ".txt" && return "asciidoc"
ext == ".txt" && return "asciidoc"
return "pandoc"
end
function run_chunk(chunk::CodeChunk, doc::WeaveDoc, report::Report, SandBox::Module)
@info("Weaving chunk $(chunk.number) from line $(chunk.start_line)")
result_chunks = eval_chunk(chunk, report, SandBox)
occursin("2html", report.formatdict[:doctype]) && (result_chunks = embed_figures(result_chunks, report.cwd))
occursin("2html", report.formatdict[:doctype]) &&
(result_chunks = embed_figures(result_chunks, report.cwd))
return result_chunks
end
@ -161,7 +161,7 @@ function embed_figures(chunk::CodeChunk, cwd)
end
function embed_figures(result_chunks, cwd)
for i in 1:length(result_chunks)
for i = 1:length(result_chunks)
figs = result_chunks[i].figures
if !isempty(figs)
result_chunks[i].figures = [img2base64(fig, cwd) for fig in figs]
@ -171,23 +171,23 @@ function embed_figures(result_chunks, cwd)
end
function img2base64(fig, cwd)
ext = splitext(fig)[2]
f = open(joinpath(cwd, fig), "r")
ext = splitext(fig)[2]
f = open(joinpath(cwd, fig), "r")
raw = read(f)
close(f)
if ext == ".png"
return "data:image/png;base64," * stringmime(MIME("image/png"), raw)
elseif ext == ".svg"
return "data:image/svg+xml;base64," * stringmime(MIME("image/svg"), raw)
elseif ext == ".gif"
return "data:image/gif;base64," * stringmime(MIME("image/gif"), raw)
else
return(fig)
end
close(f)
if ext == ".png"
return "data:image/png;base64," * stringmime(MIME("image/png"), raw)
elseif ext == ".svg"
return "data:image/svg+xml;base64," * stringmime(MIME("image/svg"), raw)
elseif ext == ".gif"
return "data:image/gif;base64," * stringmime(MIME("image/gif"), raw)
else
return (fig)
end
end
function run_chunk(chunk::DocChunk, doc::WeaveDoc, report::Report, SandBox::Module)
chunk.content = [run_inline(c, doc, report, SandBox) for c in chunk.content]
chunk.content = [run_inline(c, doc, report, SandBox) for c in chunk.content]
return chunk
end
@ -196,13 +196,14 @@ function run_inline(inline::InlineText, doc::WeaveDoc, report::Report, SandBox::
end
function run_inline(inline::InlineCode, doc::WeaveDoc, report::Report, SandBox::Module)
#Make a temporary CodeChunk for running code. Collect results and don't wrap
# 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(doc.chunk_defaults, chunk.options)
merge!(chunk.options, options)
chunks = eval_chunk(chunk, report, SandBox)
occursin("2html", report.formatdict[:doctype]) && (chunks = embed_figures(chunks, report.cwd))
occursin("2html", report.formatdict[:doctype]) &&
(chunks = embed_figures(chunks, report.cwd))
output = chunks[1].output
endswith(output, "\n") && (output = output[1:end-1])
@ -221,16 +222,22 @@ end
function run_code(chunk::CodeChunk, report::Report, SandBox::Module)
expressions = parse_input(chunk.content)
N = length(expressions)
#@show expressions
# @show expressions
result_no = 1
results = ChunkOutput[ ]
results = ChunkOutput[]
for (str_expr, expr) = expressions
for (str_expr, expr) in expressions
reset_report(report)
lastline = (result_no == N)
(obj, out) = capture_output(expr, SandBox, chunk.options[:term],
chunk.options[:display], lastline, report.throw_errors)
figures = report.figures #Captured figures
(obj, out) = capture_output(
expr,
SandBox,
chunk.options[:term],
chunk.options[:display],
lastline,
report.throw_errors,
)
figures = report.figures # Captured figures
result = ChunkOutput(str_expr, out, report.cur_result, report.rich_output, figures)
report.rich_output = ""
push!(results, result)
@ -241,9 +248,8 @@ end
getstdout() = stdout
function capture_output(expr, SandBox::Module, term, disp,
lastline, throw_errors=false)
#oldSTDOUT = STDOUT
function capture_output(expr, SandBox::Module, term, disp, lastline, throw_errors = false)
# oldSTDOUT = STDOUT
oldSTDOUT = getstdout()
out = nothing
obj = nothing
@ -253,8 +259,8 @@ function capture_output(expr, SandBox::Module, term, disp,
obj = Core.eval(SandBox, expr)
if (term || disp) && (typeof(expr) != Expr || expr.head != :toplevel)
obj != nothing && display(obj)
#This shows images and lone variables, result can
#Handle last line sepately
# This shows images and lone variables, result can
# Handle last line sepately
elseif lastline && obj != nothing
(typeof(expr) != Expr || expr.head != :toplevel) && display(obj)
end
@ -268,26 +274,24 @@ function capture_output(expr, SandBox::Module, term, disp,
out = fetch(reader)
close(rw)
end
out = replace(out, r"\u001b\[.*?m" => "") #Remove ANSI color codes
out = replace(out, r"\u001b\[.*?m" => "") # Remove ANSI color codes
return (obj, out)
end
#Parse chunk input to array of expressions
# Parse chunk input to array of expressions
function parse_input(input::AbstractString)
parsed = Tuple{AbstractString, Any}[]
parsed = Tuple{AbstractString,Any}[]
input = lstrip(input)
n = sizeof(input)
pos = 1 #The first character is extra line end
pos = 1 # The first character is extra line end
while pos n
oldpos = pos
code, pos = Meta.parse(input, pos)
push!(parsed, (input[oldpos:pos-1] , code ))
code, pos = Meta.parse(input, pos)
push!(parsed, (input[oldpos:pos-1], code))
end
parsed
end
function eval_chunk(chunk::CodeChunk, report::Report, SandBox::Module)
if !chunk.options[:eval]
chunk.output = ""
@ -295,9 +299,9 @@ function eval_chunk(chunk::CodeChunk, report::Report, SandBox::Module)
return chunk
end
#Run preexecute_hooks
# Run preexecute_hooks
for hook in preexecute_hooks
chunk = Base.invokelatest(hook, chunk)
chunk = Base.invokelatest(hook, chunk)
end
report.fignum = 1
@ -309,10 +313,9 @@ function eval_chunk(chunk::CodeChunk, report::Report, SandBox::Module)
chunk.result = run_code(chunk, report, SandBox)
#Run post_execute chunks
# Run post_execute chunks
for hook in postexecute_hooks
chunk = Base.invokelatest(hook, chunk)
chunk = Base.invokelatest(hook, chunk)
end
if chunk.options[:term]
@ -323,28 +326,29 @@ function eval_chunk(chunk::CodeChunk, report::Report, SandBox::Module)
chunks = collect_results(chunk, ScriptResult())
end
#else
# chunk.options[:fig] && (chunk.figures = copy(report.figures))
#end
# else
# chunk.options[:fig] && (chunk.figures = copy(report.figures))
# end
chunks
end
#function eval_chunk(chunk::DocChunk, report::Report, SandBox)
# function eval_chunk(chunk::DocChunk, report::Report, SandBox)
# chunk
#end
# end
#Set all variables to nothing
# Set all variables to nothing
function clear_sandbox(SandBox::Module)
for name = names(SandBox, all=true)
for name in names(SandBox, all = true)
if name != :eval && name != names(SandBox)[1]
try eval(SandBox, Meta.parse(AbstractString(AbstractString(name), "=nothing"))) catch; end
try
eval(SandBox, Meta.parse(AbstractString(AbstractString(name), "=nothing")))
catch
end
end
end
end
function get_figname(report::Report, chunk; fignum = nothing, ext = nothing)
figpath = joinpath(report.cwd, chunk.options[:fig_path])
isdir(figpath) || mkpath(figpath)
@ -352,20 +356,23 @@ function get_figname(report::Report, chunk; fignum = nothing, ext = nothing)
fignum == nothing && (fignum = report.fignum)
chunkid = (chunk.options[:label] == nothing) ? chunk.number : chunk.options[:label]
full_name = joinpath(report.cwd, chunk.options[:fig_path],
"$(report.basename)_$(chunkid)_$(fignum)$ext")
rel_name = "$(chunk.options[:fig_path])/$(report.basename)_$(chunkid)_$(fignum)$ext" #Relative path is used in output
full_name = joinpath(
report.cwd,
chunk.options[:fig_path],
"$(report.basename)_$(chunkid)_$(fignum)$ext",
)
rel_name = "$(chunk.options[:fig_path])/$(report.basename)_$(chunkid)_$(fignum)$ext" # Relative path is used in output
return full_name, rel_name
end
function get_cwd(doc::WeaveDoc, out_path)
#Set the output directory
# Set the output directory
if out_path == :doc
cwd = doc.path
elseif out_path == :pwd
cwd = pwd()
else
#If there is no extension, use as path
# If there is no extension, use as path
splitted = splitext(out_path)
if splitted[2] == ""
cwd = expanduser(out_path)
@ -376,14 +383,12 @@ function get_cwd(doc::WeaveDoc, out_path)
return cwd
end
"""Get output file name based on out_path"""
function get_outname(out_path::Symbol, doc::WeaveDoc; ext = nothing)
ext == nothing && (ext = doc.format.formatdict[:extension])
outname = "$(doc.cwd)/$(doc.basename).$ext"
end
"""Get output file name based on out_path"""
function get_outname(out_path::AbstractString, doc::WeaveDoc; ext = nothing)
ext == nothing && (ext = doc.format.formatdict[:extension])
@ -402,24 +407,30 @@ function set_rc_params(doc::WeaveDoc, fig_path, fig_ext)
else
doc.chunk_defaults[:fig_ext] = fig_ext
end
doc.chunk_defaults[:fig_path] = fig_path
doc.chunk_defaults[:fig_path] = fig_path
return nothing
end
function collect_results(chunk::CodeChunk, fmt::ScriptResult)
content = ""
result_no = 1
result_chunks = CodeChunk[ ]
for r = chunk.result
#Check if there is any output from chunk
if strip(r.stdout) == "" && isempty(r.figures) && strip(r.rich_output) == ""
result_chunks = CodeChunk[]
for r in chunk.result
# Check if there is any output from chunk
if strip(r.stdout) == "" && isempty(r.figures) && strip(r.rich_output) == ""
content *= r.code
else
content = "\n" * content * r.code
rchunk = CodeChunk(content, chunk.number, chunk.start_line, chunk.optionstring, copy(chunk.options))
rchunk = CodeChunk(
content,
chunk.number,
chunk.start_line,
chunk.optionstring,
copy(chunk.options),
)
content = ""
rchunk.result_no = result_no
result_no *=1
result_no *= 1
rchunk.figures = r.figures
rchunk.output = r.stdout * r.displayed
rchunk.rich_output = r.rich_output
@ -428,7 +439,13 @@ function collect_results(chunk::CodeChunk, fmt::ScriptResult)
end
if content != ""
startswith(content, "\n") || (content = "\n" * content)
rchunk = CodeChunk(content, chunk.number, chunk.start_line, chunk.optionstring, copy(chunk.options))
rchunk = CodeChunk(
content,
chunk.number,
chunk.start_line,
chunk.optionstring,
copy(chunk.options),
)
push!(result_chunks, rchunk)
end
@ -439,12 +456,18 @@ function collect_results(chunk::CodeChunk, fmt::TermResult)
output = ""
prompt = chunk.options[:prompt]
result_no = 1
result_chunks = CodeChunk[ ]
for r = chunk.result
result_chunks = CodeChunk[]
for r in chunk.result
output *= prompt * r.code
output *= r.displayed * r.stdout
if !isempty(r.figures)
rchunk = CodeChunk("", chunk.number, chunk.start_line, chunk.optionstring, copy(chunk.options))
rchunk = CodeChunk(
"",
chunk.number,
chunk.start_line,
chunk.optionstring,
copy(chunk.options),
)
rchunk.output = output
output = ""
rchunk.figures = r.figures
@ -452,8 +475,14 @@ function collect_results(chunk::CodeChunk, fmt::TermResult)
end
end
if output != ""
rchunk = CodeChunk("", chunk.number, chunk.start_line, chunk.optionstring, copy(chunk.options))
rchunk.output = output
rchunk = CodeChunk(
"",
chunk.number,
chunk.start_line,
chunk.optionstring,
copy(chunk.options),
)
rchunk.output = output
push!(result_chunks, rchunk)
end
@ -462,8 +491,8 @@ end
function collect_results(chunk::CodeChunk, fmt::CollectResult)
result_no = 1
for r =chunk.result
chunk.output *= r.stdout
for r in chunk.result
chunk.output *= r.stdout
chunk.rich_output *= r.rich_output
chunk.figures = [chunk.figures; r.figures]
end

View File

@ -1,33 +1,29 @@
import JSON
import Mustache
mutable struct NotebookOutput
end
mutable struct NotebookOutput end
mutable struct MarkdownOutput
end
mutable struct MarkdownOutput end
mutable struct NowebOutput
end
mutable struct NowebOutput end
mutable struct ScriptOutput
end
mutable struct ScriptOutput end
const output_formats = Dict{String, Any}(
"noweb" => NowebOutput(),
"notebook" => NotebookOutput(),
"markdown" => MarkdownOutput(),
"script" => ScriptOutput()
const output_formats = Dict{String,Any}(
"noweb" => NowebOutput(),
"notebook" => NotebookOutput(),
"markdown" => MarkdownOutput(),
"script" => ScriptOutput(),
)
"""Autodetect format for converter"""
function detect_outformat(outfile::String)
ext = lowercase(splitext(outfile)[2])
ext = lowercase(splitext(outfile)[2])
ext == ".jl" && return "script"
ext == ".jmd" && return "markdown"
ext == ".ipynb" && return "notebook"
return "noweb"
ext == ".jl" && return "script"
ext == ".jmd" && return "markdown"
ext == ".ipynb" && return "notebook"
return "noweb"
end
"""
@ -39,37 +35,41 @@ Convert Weave documents between different formats
- `outfile`: Path of the output document
- `format = nothing`: Output document format (optional). It will be detected automatically from the `outfile` extension. You can also specify either of `"script"`, `"markdown"`, `"notebook"`, or `"noweb"`
"""
function convert_doc(infile::AbstractString, outfile::AbstractString; format::Union{Nothing,AbstractString} = nothing)
doc = read_doc(infile)
function convert_doc(
infile::AbstractString,
outfile::AbstractString;
format::Union{Nothing,AbstractString} = nothing,
)
doc = read_doc(infile)
if format == nothing
format = detect_outformat(outfile)
end
if format == nothing
format = detect_outformat(outfile)
end
converted = convert_doc(doc, output_formats[format])
converted = convert_doc(doc, output_formats[format])
open(outfile, "w") do f
write(f, converted)
end
open(outfile, "w") do f
write(f, converted)
end
return nothing
return nothing
end
"""Convert Weave document to Jupyter notebook format"""
function convert_doc(doc::WeaveDoc, format::NotebookOutput)
nb = Dict()
nb["nbformat"] = 4
nb["nbformat"] = 4
nb["nbformat_minor"] = 2
metadata = Dict()
kernelspec = Dict()
kernelspec["language"] = "julia"
kernelspec["name"] = "julia-$(VERSION.major).$(VERSION.minor)"
kernelspec["language"] = "julia"
kernelspec["name"] = "julia-$(VERSION.major).$(VERSION.minor)"
kernelspec["display_name"] = "Julia $(VERSION.major).$(VERSION.minor).$(VERSION.patch)"
metadata["kernelspec"] = kernelspec
language_info = Dict()
language_info["file_extension"] = ".jl"
language_info["mimetype"] = "application/julia"
language_info["name"]= "julia"
language_info["name"] = "julia"
language_info["version"] = "$(VERSION.major).$(VERSION.minor).$(VERSION.patch)"
metadata["language_info"] = language_info
cells = []
@ -84,30 +84,35 @@ function convert_doc(doc::WeaveDoc, format::NotebookOutput)
if isa(doc.chunks[1], DocChunk)
strip_header!(doc)
doc.chunks[1].content[1].content = Mustache.render(head_tpl;
[Pair(Symbol(k), v) for (k,v) in doc.header]...) * doc.chunks[1].content[1].content
doc.chunks[1].content[1].content =
Mustache.render(head_tpl; [Pair(Symbol(k), v) for (k, v) in doc.header]...) * doc.chunks[1].content[1].content
end
for chunk in doc.chunks
if isa(chunk, DocChunk)
push!(cells,
Dict("cell_type" => "markdown",
"metadata" => Dict(),
"source" => [strip(join([repr(c) for c in chunk.content], ""))])
)
push!(
cells,
Dict(
"cell_type" => "markdown",
"metadata" => Dict(),
"source" => [strip(join([repr(c) for c in chunk.content], ""))],
),
)
elseif haskey(chunk.options, :skip) && chunk.options[:skip] == "notebook"
continue
else
push!(cells,
Dict("cell_type" => "code",
"metadata" => Dict(),
"source" => [strip(chunk.content)],
"execution_count" => nothing,
"outputs" => []
))
end
end
push!(
cells,
Dict(
"cell_type" => "code",
"metadata" => Dict(),
"source" => [strip(chunk.content)],
"execution_count" => nothing,
"outputs" => [],
),
)
end
end
nb["cells"] = cells
nb["metadata"] = metadata
@ -118,61 +123,61 @@ end
"""Convert Weave document to Jupyter notebook format"""
function convert_doc(doc::WeaveDoc, format::MarkdownOutput)
output = ""
for chunk in doc.chunks
if isa(chunk, DocChunk)
output *= join([repr(c) for c in chunk.content], "")
else
output *= "\n" * "```julia"
isempty(chunk.optionstring) || (output *= ";" * chunk.optionstring)
output *= "\n" * lstrip(chunk.content)
output *= "```\n"
output = ""
for chunk in doc.chunks
if isa(chunk, DocChunk)
output *= join([repr(c) for c in chunk.content], "")
else
output *= "\n" * "```julia"
isempty(chunk.optionstring) || (output *= ";" * chunk.optionstring)
output *= "\n" * lstrip(chunk.content)
output *= "```\n"
end
end
end
return output
return output
end
"""Convert Weave document to noweb format"""
function convert_doc(doc::WeaveDoc, format::NowebOutput)
output = ""
for chunk in doc.chunks
if isa(chunk, DocChunk)
output *= join([repr(c) for c in chunk.content], "")
else
output *= "\n" * "<<"
isempty(chunk.optionstring) || (output *= strip(chunk.optionstring))
output *= ">>="
output *= "\n" * lstrip(chunk.content)
output *= "@\n"
output = ""
for chunk in doc.chunks
if isa(chunk, DocChunk)
output *= join([repr(c) for c in chunk.content], "")
else
output *= "\n" * "<<"
isempty(chunk.optionstring) || (output *= strip(chunk.optionstring))
output *= ">>="
output *= "\n" * lstrip(chunk.content)
output *= "@\n"
end
end
end
return output
return output
end
"""Convert Weave document to script format"""
function convert_doc(doc::WeaveDoc, format::ScriptOutput)
output = ""
for chunk in doc.chunks
if typeof(chunk) == Weave.DocChunk
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))
output *= "\n\n" * lstrip(chunk.content)
output *= "\n"
output = ""
for chunk in doc.chunks
if typeof(chunk) == Weave.DocChunk
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))
output *= "\n\n" * lstrip(chunk.content)
output *= "\n"
end
end
end
return output
return output
end
function Base.repr(c::InlineText)
return c.content
return c.content
end
function Base.repr(c::InlineCode)
return "`j $(c.content)`"
return "`j $(c.content)`"
end