refactoring:

- reorder/move things so that format-specific rendering methods are 
defined close to the format
- separate common rendering methods from format specific methods
- fix using location
pull/350/head
Shuhei Kadowaki 2020-06-01 21:32:18 +09:00
parent 543f99a915
commit ae0dc70ffa
7 changed files with 223 additions and 232 deletions

View File

@ -1,7 +1,6 @@
module Weave
using Highlights, Mustache, Requires
using Printf
using Highlights, Mustache, Requires, Pkg
const PKG_DIR = normpath(@__DIR__, "..")

View File

@ -1,9 +1,10 @@
# fallback methods
# ----------------
# TODO: is there any other format where we want to restore headers ?
# make this field of format struct
const HEADER_PRESERVE_DOCTYPES = ("github", "hugo")
function restore_header!(doc)
doc.doctype in HEADER_PRESERVE_DOCTYPES || return # don't restore
@ -30,15 +31,6 @@ function format_inline(inline::InlineCode)
return inline.output
end
function clear_buffer_and_format!(io::IOBuffer, out::IOBuffer, render_function)
text = take2string!(io)
m = Markdown.parse(text, flavor = WeaveMarkdown.weavemd)
write(out, string(render_function(m)))
end
addlines(op, inline) = inline.ctype === :line ? string('\n', op, '\n') : op
function format_chunk(chunk::CodeChunk, docformat)
# Fill undefined options with format specific defaults
@ -110,29 +102,6 @@ end
format_code(code, docformat) = code
format_output(result, docformat) = result
function format_termchunk(chunk, docformat)
return if should_render(chunk)
string(docformat.termstart, chunk.output, '\n', docformat.termend, '\n')
else
""
end
end
should_render(chunk) = chunk.options[:echo] && chunk.options[:results] "hidden"
highlight_code(mime, code, highlight_theme) =
highlight(mime, strip(code), Highlights.Lexers.JuliaLexer, highlight_theme)
highlight_term(mime, output, highlight_theme) =
highlight(mime, strip(output), Highlights.Lexers.JuliaConsoleLexer, highlight_theme)
highlight(mime, output, lexer, theme = Highlights.Themes.DefaultTheme) =
sprint((io, x) -> Highlights.highlight(io, mime, x, lexer, theme), output)
indent(text, nindent) = join(map(x -> string(repeat(' ', nindent), x), split(text, '\n')), '\n')
function wraplines(text, line_width = 75)
@ -157,3 +126,46 @@ function wrapline(text, line_width = 75)
end
result *= text
end
format_output(result, docformat) = result
function format_termchunk(chunk, docformat)
return if should_render(chunk)
string(docformat.termstart, chunk.output, '\n', docformat.termend, '\n')
else
""
end
end
should_render(chunk) = chunk.options[:echo] && chunk.options[:results] "hidden"
render_doc(_, body, args...; kwargs...) = body
# utilities
# ---------
function clear_buffer_and_format!(io::IOBuffer, out::IOBuffer, render_function)
text = take2string!(io)
m = Markdown.parse(text, flavor = WeaveMarkdown.weavemd)
write(out, string(render_function(m)))
end
addlines(op, inline) = inline.ctype === :line ? string('\n', op, '\n') : op
get_template(path::AbstractString) = Mustache.template_from_file(path)
get_template(tpl::Mustache.MustacheTokens) = tpl
get_highlight_stylesheet(mime, highlight_theme) =
get_highlight_stylesheet(mime, get_highlight_theme(highlight_theme))
get_highlight_stylesheet(mime, highlight_theme::Type{<:Highlights.AbstractTheme}) =
sprint((io, x) -> Highlights.stylesheet(io, mime, x), highlight_theme)
get_highlight_theme(::Nothing) = Highlights.Themes.DefaultTheme
get_highlight_theme(highlight_theme::Type{<:Highlights.AbstractTheme}) = highlight_theme
highlight_code(mime, code, highlight_theme) =
highlight(mime, strip(code), Highlights.Lexers.JuliaLexer, highlight_theme)
highlight_term(mime, output, highlight_theme) =
highlight(mime, strip(output), Highlights.Lexers.JuliaConsoleLexer, highlight_theme)
highlight(mime, output, lexer, theme = Highlights.Themes.DefaultTheme) =
sprint((io, x) -> Highlights.highlight(io, mime, x, lexer, theme), output)

View File

@ -1,5 +1,6 @@
# HTML
# ----
abstract type HTMLFormat <: WeaveFormat end
Base.@kwdef mutable struct JMarkdown2HTML <: HTMLFormat
@ -46,24 +47,6 @@ Base.@kwdef mutable struct Pandoc2HTML <: HTMLFormat
end
register_format!("pandoc2html", Pandoc2HTML())
function render_doc(docformat::JMarkdown2HTML, body, doc, css)
_, weave_source = splitdir(abspath(doc.source))
weave_version, weave_date = weave_info()
return Mustache.render(
get_html_template(docformat.template);
body = body,
stylesheet = get_stylesheet(css),
highlight_stylesheet = get_highlight_stylesheet(MIME("text/html"), docformat.highlight_theme),
header_script = doc.header_script,
weave_source = weave_source,
weave_version = weave_version,
weave_date = weave_date,
[Pair(Symbol(k), v) for (k, v) in doc.header]...,
)
end
# very similar to tex version of function
function format_chunk(chunk::DocChunk, docformat::JMarkdown2HTML)
out = IOBuffer()
@ -132,3 +115,25 @@ function formatfigures(chunk, docformat::JMarkdown2HTML)
return result
end
function render_doc(docformat::JMarkdown2HTML, body, doc; css = nothing)
_, weave_source = splitdir(abspath(doc.source))
weave_version, weave_date = weave_info()
return Mustache.render(
get_html_template(docformat.template);
body = body,
stylesheet = get_stylesheet(css),
highlight_stylesheet = get_highlight_stylesheet(MIME("text/html"), docformat.highlight_theme),
header_script = doc.header_script,
weave_source = weave_source,
weave_version = weave_version,
weave_date = weave_date,
[Pair(Symbol(k), v) for (k, v) in doc.header]...,
)
end
get_stylesheet(::Nothing) = get_stylesheet(normpath(STYLESHEET_DIR, "skeleton.css"))
get_stylesheet(path::AbstractString) = read(path, String)
get_html_template(::Nothing) = get_template(normpath(TEMPLATE_DIR, "md2html.tpl"))
get_html_template(x) = get_template(x)

View File

@ -1,6 +1,7 @@
abstract type MarkdownFormat <: WeaveFormat end
# markdown
# --------
# GitHub markdown
# ---------------
Base.@kwdef mutable struct GitHubMarkdown <: MarkdownFormat
description = "GitHub markdown"
@ -25,6 +26,31 @@ Base.@kwdef mutable struct GitHubMarkdown <: MarkdownFormat
end
register_format!("github", GitHubMarkdown())
function formatfigures(chunk, docformat::GitHubMarkdown)
fignames = chunk.figures
caption = chunk.options[:fig_cap]
result = ""
figstring = ""
length(fignames) > 0 || (return "")
if caption != nothing
result *= "![$caption]($(fignames[1]))\n"
for fig in fignames[2:end]
result *= "![]($fig)\n"
println("Warning, only the first figure gets a caption\n")
end
else
for fig in fignames
result *= "![]($fig)\n"
end
end
return result
end
# Hugo markdown
# -------------
Base.@kwdef mutable struct Hugo <: MarkdownFormat
description = "Hugo markdown (using shortcodes)"
codestart = "````julia"
@ -48,6 +74,23 @@ Base.@kwdef mutable struct Hugo <: MarkdownFormat
end
register_format!("hugo", Hugo())
function formatfigures(chunk, docformat::Hugo)
relpath = docformat.uglyURLs ? "" : ".."
mapreduce(*, enumerate(chunk.figures), init = "") do (index, fig)
if index > 1
@warn("Only the first figure gets a caption.")
title_spec = ""
else
caption = chunk.options[:fig_cap]
title_spec = caption == nothing ? "" : "title=\"$(caption)\" "
end
"{{< figure src=\"$(joinpath(relpath, fig))\" $(title_spec) >}}"
end
end
# multi language markdown
# -----------------------
Base.@kwdef mutable struct MultiMarkdown <: MarkdownFormat
description = "MultiMarkdown"
codestart = "````julia"
@ -70,71 +113,6 @@ Base.@kwdef mutable struct MultiMarkdown <: MarkdownFormat
end
register_format!("multimarkdown", MultiMarkdown())
# pandoc
# ------
Base.@kwdef mutable struct Pandoc <: MarkdownFormat
description = "Pandoc markdown"
codestart = "~~~~{.julia}"
codeend = "~~~~~~~~~~~~~\n\n"
outputstart = "~~~~"
outputend = "~~~~\n\n"
fig_ext = ".png"
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"]
keep_unicode = false
termstart = codestart
termend = codeend
out_width = nothing
out_height = nothing
fig_pos = nothing
fig_env = nothing
highlight_theme = nothing
template = nothing
end
register_format!("pandoc", Pandoc())
register_format!("pandoc2pdf", Pandoc())
function formatfigures(chunk, docformat::GitHubMarkdown)
fignames = chunk.figures
caption = chunk.options[:fig_cap]
result = ""
figstring = ""
length(fignames) > 0 || (return "")
if caption != nothing
result *= "![$caption]($(fignames[1]))\n"
for fig in fignames[2:end]
result *= "![]($fig)\n"
println("Warning, only the first figure gets a caption\n")
end
else
for fig in fignames
result *= "![]($fig)\n"
end
end
return result
end
function formatfigures(chunk, docformat::Hugo)
relpath = docformat.uglyURLs ? "" : ".."
mapreduce(*, enumerate(chunk.figures), init = "") do (index, fig)
if index > 1
@warn("Only the first figure gets a caption.")
title_spec = ""
else
caption = chunk.options[:fig_cap]
title_spec = caption == nothing ? "" : "title=\"$(caption)\" "
end
"{{< figure src=\"$(joinpath(relpath, fig))\" $(title_spec) >}}"
end
end
function formatfigures(chunk, docformat::MultiMarkdown)
fignames = chunk.figures
caption = chunk.options[:fig_cap]
@ -165,3 +143,63 @@ function formatfigures(chunk, docformat::MultiMarkdown)
end
return result
end
# pandoc
# ------
Base.@kwdef mutable struct Pandoc <: MarkdownFormat
description = "Pandoc markdown"
codestart = "~~~~{.julia}"
codeend = "~~~~~~~~~~~~~\n\n"
outputstart = "~~~~"
outputend = "~~~~\n\n"
fig_ext = ".png"
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"]
keep_unicode = false
termstart = codestart
termend = codeend
out_width = nothing
out_height = nothing
fig_pos = nothing
fig_env = nothing
highlight_theme = nothing
template = nothing
end
register_format!("pandoc", Pandoc())
register_format!("pandoc2pdf", Pandoc())
function formatfigures(chunk, docformat::Pandoc)
fignames = chunk.figures
length(fignames) > 0 || (return "")
caption = chunk.options[:fig_cap]
label = get(chunk.options, :label, nothing)
result = ""
figstring = ""
attribs = ""
width = chunk.options[:out_width]
height = chunk.options[:out_height]
# Build figure attibutes
attribs = String[]
width == nothing || push!(attribs, "width=$width")
height == nothing || push!(attribs, "height=$height")
label == nothing || push!(attribs, "#fig:$label")
attribs = isempty(attribs) ? "" : "{" * join(attribs, " ") * "}"
if caption != nothing
result *= "![$caption]($(fignames[1]))$attribs\n"
for fig in fignames[2:end]
result *= "![]($fig)$attribs\n"
println("Warning, only the first figure gets a caption\n")
end
else
for fig in fignames
result *= "![]($fig)$attribs\\ \n\n"
end
end
return result
end

View File

@ -4,7 +4,7 @@
# - 3. Export new interface
# - 4. Document Interface
using Mustache, Highlights, .WeaveMarkdown, Markdown, Dates, Pkg
using Mustache, Highlights, .WeaveMarkdown, Markdown, Dates, Printf
using REPL.REPLCompletions: latex_symbols
@ -14,7 +14,7 @@ const FORMATS = Dict{String,WeaveFormat}()
register_format!(format_name::AbstractString, format::WeaveFormat) = push!(FORMATS, format_name => format)
register_format!(_,format) = error("Format needs to be a subtype of WeaveFormat.")
function format(doc; css = nothing)
function format(doc; kwargs...)
docformat = doc.format
restore_header!(doc)
@ -24,30 +24,9 @@ function format(doc; css = nothing)
end
body = join(lines, '\n')
return render_doc(docformat, body, doc, css)
return render_doc(docformat, body, doc; kwargs...)
end
render_doc(_, body, args...) = body
get_highlight_theme(::Nothing) = Highlights.Themes.DefaultTheme
get_highlight_theme(highlight_theme::Type{<:Highlights.AbstractTheme}) = highlight_theme
get_html_template(::Nothing) = get_template(normpath(TEMPLATE_DIR, "md2html.tpl"))
get_html_template(x) = get_template(x)
get_tex_template(::Nothing) = get_template(normpath(TEMPLATE_DIR, "md2pdf.tpl"))
get_tex_template(x) = get_template(x)
get_template(path::AbstractString) = Mustache.template_from_file(path)
get_template(tpl::Mustache.MustacheTokens) = tpl
get_stylesheet(::Nothing) = get_stylesheet(normpath(STYLESHEET_DIR, "skeleton.css"))
get_stylesheet(path::AbstractString) = read(path, String)
get_highlight_stylesheet(mime, highlight_theme) =
get_highlight_stylesheet(mime, get_highlight_theme(highlight_theme))
get_highlight_stylesheet(mime, highlight_theme::Type{<:Highlights.AbstractTheme}) =
sprint((io, x) -> Highlights.stylesheet(io, mime, x), highlight_theme)
include("common.jl")
include("htmlformats.jl")

View File

@ -71,16 +71,6 @@ Base.@kwdef mutable struct TexMinted <: TexFormat
end
register_format!("texminted", TexMinted())
function render_doc(docformat::JMarkdown2tex, body, doc, _)
return Mustache.render(
get_tex_template(docformat.template);
body = body,
highlight = get_highlight_stylesheet(MIME("text/latex"), docformat.highlight_theme),
[Pair(Symbol(k), v) for (k, v) in doc.header]...,
)
end
# very similar to export to html
function format_chunk(chunk::DocChunk, docformat::JMarkdown2tex)
out = IOBuffer()
@ -119,7 +109,6 @@ function format_code(code, docformat::JMarkdown2tex)
return ret
end
# Convert unicode to tex, escape listings if needed
function uc2tex(s, escape = false)
for key in keys(latex_symbols)
@ -132,11 +121,24 @@ function uc2tex(s, escape = false)
return s
end
#should_render(chunk) ? highlight_term(MIME("text/latex"), , docformat.highlight_theme) : ""
format_termchunk(chunk, docformat::JMarkdown2tex) =
should_render(chunk) ? highlight_term(MIME("text/latex"), chunk.output, docformat.highlight_theme) : ""
# Make julia symbols (\bf* etc.) valid latex
function texify(s)
return if occursin(r"^\\bf[A-Z]$", s)
replace(s, "\\bf" => "\\bm{\\mathrm{") * "}}"
elseif startswith(s, "\\bfrak")
replace(s, "\\bfrak" => "\\bm{\\mathfrak{") * "}}"
elseif startswith(s, "\\bf")
replace(s, "\\bf" => "\\bm{\\") * "}"
elseif startswith(s, "\\frak")
replace(s, "\\frak" => "\\mathfrak{") * "}"
else
s
end
end
function formatfigures(chunk, docformat::TexFormat)
fignames = chunk.figures
@ -196,7 +198,6 @@ function formatfigures(chunk, docformat::TexFormat)
return result
end
function md_length_to_latex(def, reference)
if occursin("%", def)
_def = tryparse(Float64, replace(def, "%" => ""))
@ -207,18 +208,14 @@ function md_length_to_latex(def, reference)
return def
end
# Make julia symbols (\bf* etc.) valid latex
function texify(s)
return if occursin(r"^\\bf[A-Z]$", s)
replace(s, "\\bf" => "\\bm{\\mathrm{") * "}}"
elseif startswith(s, "\\bfrak")
replace(s, "\\bfrak" => "\\bm{\\mathfrak{") * "}}"
elseif startswith(s, "\\bf")
replace(s, "\\bf" => "\\bm{\\") * "}"
elseif startswith(s, "\\frak")
replace(s, "\\frak" => "\\mathfrak{") * "}"
else
s
end
function render_doc(docformat::JMarkdown2tex, body, doc; kwargs...)
return Mustache.render(
get_tex_template(docformat.template);
body = body,
highlight = get_highlight_stylesheet(MIME("text/latex"), docformat.highlight_theme),
[Pair(Symbol(k), v) for (k, v) in doc.header]...,
)
end
get_tex_template(::Nothing) = get_template(normpath(TEMPLATE_DIR, "md2pdf.tpl"))
get_tex_template(x) = get_template(x)

View File

@ -25,6 +25,28 @@ Base.@kwdef mutable struct Rest <: WeaveFormat
end
register_format!("rst", Rest())
function formatfigures(chunk, docformat::Rest)
fignames = chunk.figures
caption = chunk.options[:fig_cap]
width = chunk.options[:out_width]
result = ""
figstring = ""
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",
)
else
result *= figstring
return result
end
end
# Ansii
# -----
@ -52,67 +74,6 @@ Base.@kwdef mutable struct AsciiDoc <: WeaveFormat
end
register_format!("asciidoc", AsciiDoc())
function formatfigures(chunk, docformat::Pandoc)
fignames = chunk.figures
length(fignames) > 0 || (return "")
caption = chunk.options[:fig_cap]
label = get(chunk.options, :label, nothing)
result = ""
figstring = ""
attribs = ""
width = chunk.options[:out_width]
height = chunk.options[:out_height]
# Build figure attibutes
attribs = String[]
width == nothing || push!(attribs, "width=$width")
height == nothing || push!(attribs, "height=$height")
label == nothing || push!(attribs, "#fig:$label")
attribs = isempty(attribs) ? "" : "{" * join(attribs, " ") * "}"
if caption != nothing
result *= "![$caption]($(fignames[1]))$attribs\n"
for fig in fignames[2:end]
result *= "![]($fig)$attribs\n"
println("Warning, only the first figure gets a caption\n")
end
else
for fig in fignames
result *= "![]($fig)$attribs\\ \n\n"
end
end
return result
end
function formatfigures(chunk, docformat::Rest)
fignames = chunk.figures
caption = chunk.options[:fig_cap]
width = chunk.options[:out_width]
result = ""
figstring = ""
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",
)
else
result *= figstring
return result
end
end
function formatfigures(chunk, docformat::AsciiDoc)
fignames = chunk.figures
caption = chunk.options[:fig_cap]