diff --git a/src/Weave.jl b/src/Weave.jl index 2b354f5..e74e653 100644 --- a/src/Weave.jl +++ b/src/Weave.jl @@ -27,15 +27,11 @@ end take2string!(io) = String(take!(io)) """ - list_out_formats() + list_out_formats(io = stdout) List supported output formats """ -function list_out_formats() - for format in keys(formats) - println(string(format, ": ", formats[format].description)) - end -end +list_out_formats(io = stdout) = for (k, v) in FORMATS; println(io, string(k, ": ", v.description)); end """ tangle(source::AbstractString; kwargs...) @@ -228,7 +224,7 @@ weave(doc::AbstractString, doctype::Union{Symbol,AbstractString}; kwargs...) = weave(doc; doctype = doctype, kwargs...) function specific_options!(weave_options, doctype) - fmts = keys(formats) + fmts = keys(FORMATS) for (k,v) in weave_options if k in fmts k == doctype && merge!(weave_options, v) @@ -335,7 +331,7 @@ include("display_methods.jl") include("reader/reader.jl") include("run.jl") include("cache.jl") -include("formatters.jl") +include("formats.jl") include("format.jl") include("pandoc.jl") include("converter.jl") diff --git a/src/formatters.jl b/src/formats.jl similarity index 78% rename from src/formatters.jl rename to src/formats.jl index f3cf809..551df24 100644 --- a/src/formatters.jl +++ b/src/formats.jl @@ -1,16 +1,105 @@ -# so dirty, refactor - -using Printf +# TODO: +# - 1. turn each `.formatdict` value into fields +# - 2. do assertions for definition mandatory fields in `@define_format` macro +# - 3. export as public API -struct Tex - description::AbstractString - formatdict::Dict{Symbol,Any} +abstract type WeaveFormat end +const FORMATS = Dict{String,WeaveFormat}() +register_format!(format_name::AbstractString, format::WeaveFormat) = push!(FORMATS, format_name => format) + +macro define_format(type_name, supertype = WeaveFormat) + return quote + struct $(type_name) <: $(supertype) + description::String + formatdict::Dict{Symbol,Any} + end + end end -const tex = Tex( + +# HTML +# ---- + +@define_format JMarkdown2HTML +register_format!("md2html", JMarkdown2HTML( + "Julia markdown to html", + Dict( + :codestart => "\n", + :codeend => "\n", + :outputstart => "
",
+        :outputend => "
\n", + :fig_ext => ".png", + :mimetypes => [ + "image/png", + "image/jpg", + "image/svg+xml", + "text/html", + "text/markdown", + "text/plain", + ], + :extension => "html", + :doctype => "md2html", + ), +)) + +@define_format Pandoc2HTML +register_format!("pandoc2html", Pandoc2HTML( + "Markdown to HTML (requires Pandoc 2)", + Dict( + :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", + ), +)) + + +# PDF and Tex +# ----------- + +@define_format JMarkdown2tex +let t = JMarkdown2tex( + "Julia markdown to latex", + Dict( + :codestart => "", + :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, + ) + ) + register_format!("md2pdf", t) + register_format!("md2tex", t) +end + +@define_format Tex +register_format!("tex", Tex( "Latex with custom code environments", - Dict{Symbol,Any}( + Dict( :codestart => "\\begin{juliacode}", :codeend => "\\end{juliacode}", :outputstart => "\\begin{juliaout}", @@ -26,11 +115,10 @@ const tex = Tex( :mimetypes => ["application/pdf", "image/png", "text/latex", "text/plain"], :keep_unicode => false, ), -) - -const texminted = Tex( +)) +register_format!("texminted", Tex( "Latex using minted for highlighting", - Dict{Symbol,Any}( + Dict( :codestart => "\\begin{minted}[mathescape, fontsize=\\small, xleftmargin=0.5em]{julia}", :codeend => "\\end{minted}", @@ -49,64 +137,41 @@ const texminted = Tex( :mimetypes => ["application/pdf", "image/png", "text/latex", "text/plain"], :keep_unicode => false, ), -) +)) -struct Pandoc - description::AbstractString - formatdict::Dict{Symbol,Any} + +# pandoc +# ------ + +@define_format Pandoc +let p = Pandoc( + "Pandoc markdown", + Dict( + :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", + ), + ) + register_format!("pandoc", p) + register_format!("pandoc2pdf", p) 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", - ), -) -struct Pandoc2HTML - description::AbstractString - formatdict::Dict{Symbol,Any} -end +# markdown +# -------- -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} -end - -const github = GitHubMarkdown( +@define_format GitHubMarkdown +register_format!("github", GitHubMarkdown( "GitHub markdown", - Dict{Symbol,Any}( + Dict( :codestart => "````julia", :codeend => "````\n\n", :outputstart => "````", @@ -117,22 +182,12 @@ const github = GitHubMarkdown( ["image/png", "image/svg+xml", "image/jpg", "text/markdown", "text/plain"], :doctype => "github", ), -) +)) -""" -Formatter for Hugo: https://gohugo.io/ - -When `uglyURLs` is `false`, prepend figure path by `..`. -""" -struct Hugo - description::AbstractString - formatdict::Dict{Symbol,Any} - uglyURLs::Bool -end - -const hugo = Hugo( +@define_format Hugo +register_format!("hugo", Hugo( "Hugo markdown (using shortcodes)", - Dict{Symbol,Any}( + Dict( :codestart => "````julia", :codeend => "````\n\n", :outputstart => "````", @@ -140,69 +195,79 @@ const hugo = Hugo( :fig_ext => ".png", :extension => "md", :doctype => "hugo", + :uglyURLs => false, # if `false`, prepend figure path by `..` ), - false, -) +)) -# Julia markdown -struct JMarkdown2HTML - description::AbstractString - formatdict::Dict{Symbol,Any} -end - -const md2html = JMarkdown2HTML( - "Julia markdown to html", - Dict{Symbol,Any}( - :codestart => "\n", - :codeend => "\n", - :outputstart => "
",
-        :outputend => "
\n", +@define_format MultiMarkdown +register_format!("multimarkdown", MultiMarkdown( + "MultiMarkdown", + Dict( + :codestart => "````julia", + :codeend => "````\n\n", + :outputstart => "````", + :outputend => "````\n\n", :fig_ext => ".png", - :mimetypes => [ - "image/png", - "image/jpg", - "image/svg+xml", - "text/html", - "text/markdown", - "text/plain", - ], - :extension => "html", - :doctype => "md2html", + :extension => "md", + :doctype => "github", ), -) +)) -# Julia markdown -struct JMarkdown2tex - description::AbstractString - formatdict::Dict{Symbol,Any} -end -const md2tex = JMarkdown2tex( - "Julia markdown to latex", - Dict{Symbol,Any}( - :codestart => "", - :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, +# Rest +# ---- + +@define_format Rest +register_format!("rst", Rest( + "reStructuredText and Sphinx", + Dict( + :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 MultiMarkdown - description::AbstractString - formatdict::Dict{Symbol,Any} + +# Ansii +# ----- + +# asciidoc -b html5 -a source-highlighter=pygments ... +@define_format AsciiDoc +register_format!("asciidoc", AsciiDoc( + "AsciiDoc", + Dict( + :codestart => "[source,julia]\n--------------------------------------", + :codeend => "--------------------------------------\n\n", + :outputstart => "--------------------------------------", + :outputend => "--------------------------------------\n\n", + :fig_ext => ".png", + :extension => "txt", + :out_width => "600", + :doctype => "asciidoc", + ), +)) + + +# TODO: move this functions where used +# ------------------------------------ + +using Printf + + +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) + return "$perc$reference" + end + return def end function formatfigures(chunk, docformat::JMarkdown2HTML) @@ -244,69 +309,6 @@ function formatfigures(chunk, docformat::JMarkdown2HTML) 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", - ), -) - -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", - ), -) - -struct AsciiDoc - description::AbstractString - formatdict::Dict{Symbol,Any} -end - -# asciidoc -b html5 -a source-highlighter=pygments ... -const adoc = AsciiDoc( - "AsciiDoc", - Dict{Symbol,Any}( - :codestart => "[source,julia]\n--------------------------------------", - :codeend => "--------------------------------------\n\n", - :outputstart => "--------------------------------------", - :outputend => "--------------------------------------\n\n", - :fig_ext => ".png", - :extension => "txt", - :out_width => "600", - :doctype => "asciidoc", - ), -) - -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) - return "$perc$reference" - end - return def -end - function formatfigures(chunk, docformat::Union{Tex,JMarkdown2tex}) fignames = chunk.figures caption = chunk.options[:fig_cap] @@ -423,7 +425,7 @@ function formatfigures(chunk, docformat::GitHubMarkdown) end function formatfigures(chunk, docformat::Hugo) - relpath = docformat.uglyURLs ? "" : ".." + relpath = docformat.formatdict[:uglyURLs] ? "" : ".." function format_shortcode(index_and_fig) index, fig = index_and_fig if index > 1 @@ -510,20 +512,3 @@ function formatfigures(chunk, docformat::AsciiDoc) return result end end - -# Add new supported formats here -const formats = Dict( - "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, -) diff --git a/src/run.jl b/src/run.jl index 47f296b..8e07b26 100644 --- a/src/run.jl +++ b/src/run.jl @@ -45,7 +45,7 @@ function run_doc( # cache :all, :user, :off, :refresh doc.doctype = isnothing(doctype) ? (doctype = detect_doctype(doc.source)) : doctype - doc.format = deepcopy(formats[doctype]) + doc.format = deepcopy(FORMATS[doctype]) doc.cwd = get_cwd(doc, out_path) isdir(doc.cwd) || mkpath(doc.cwd) diff --git a/test/figureformatter_test.jl b/test/figureformatter_test.jl index 0a54ae5..26facec 100644 --- a/test/figureformatter_test.jl +++ b/test/figureformatter_test.jl @@ -1,5 +1,5 @@ -using Weave -using Test +test_formatfigures(chunk, format) = Weave.formatfigures(chunk, Weave.FORMATS[format]) + # Make a dummy codehunk with figure chunk = Weave.CodeChunk("plot(x)", 1, 1, "", Dict()) @@ -7,43 +7,49 @@ options = merge(Weave.get_chunk_defaults(), chunk.options) merge!(chunk.options, options) chunk.figures = ["figs/figures_plot1.png"] -@test Weave.formatfigures(chunk, Weave.md2tex) == "\\includegraphics{figs/figures_plot1.png}\n" -@test Weave.formatfigures(chunk, Weave.tex) == "\\includegraphics{figs/figures_plot1.png}\n" -@test Weave.formatfigures(chunk, Weave.texminted) == "\\includegraphics{figs/figures_plot1.png}\n" -@test Weave.formatfigures(chunk, Weave.pandoc) == "![](figs/figures_plot1.png)\\ \n\n" -@test Weave.formatfigures(chunk, Weave.github) == "![](figs/figures_plot1.png)\n" -@test Weave.formatfigures(chunk, Weave.hugo) == "{{< figure src=\"../figs/figures_plot1.png\" >}}" -@test Weave.formatfigures(chunk, Weave.multimarkdown) == "![][figs/figures_plot1.png]\n\n[figs/figures_plot1.png]: figs/figures_plot1.png \n" -@test Weave.formatfigures(chunk, Weave.md2html) == "\n" + +@test test_formatfigures(chunk, "md2tex") == "\\includegraphics{figs/figures_plot1.png}\n" +@test test_formatfigures(chunk, "tex") == "\\includegraphics{figs/figures_plot1.png}\n" +@test test_formatfigures(chunk, "texminted") == "\\includegraphics{figs/figures_plot1.png}\n" +@test test_formatfigures(chunk, "pandoc") == "![](figs/figures_plot1.png)\\ \n\n" +@test test_formatfigures(chunk, "github") == "![](figs/figures_plot1.png)\n" +@test test_formatfigures(chunk, "hugo") == "{{< figure src=\"../figs/figures_plot1.png\" >}}" +@test test_formatfigures(chunk, "multimarkdown") == "![][figs/figures_plot1.png]\n\n[figs/figures_plot1.png]: figs/figures_plot1.png \n" +@test test_formatfigures(chunk, "md2html") == "\n" + chunk.options[:out_width] = "100%" -@test Weave.formatfigures(chunk, Weave.adoc) == "image::figs/figures_plot1.png[width=100%]\n" -@test Weave.formatfigures(chunk, Weave.rst) == ".. image:: figs/figures_plot1.png\n :width: 100%\n\n" +@test test_formatfigures(chunk, "asciidoc") == "image::figs/figures_plot1.png[width=100%]\n" +@test test_formatfigures(chunk, "rst") == ".. image:: figs/figures_plot1.png\n :width: 100%\n\n" + chunk.options[:fig_cap] = "Nice plot" -@test Weave.formatfigures(chunk, Weave.tex) == "\\begin{figure}[!h]\n\\center\n\\includegraphics[width=1.0\\linewidth]{figs/figures_plot1.png}\n\\caption{Nice plot}\n\\end{figure}\n" -@test Weave.formatfigures(chunk, Weave.pandoc) == "![Nice plot](figs/figures_plot1.png){width=100%}\n" -@test Weave.formatfigures(chunk, Weave.md2tex) == "\\begin{figure}[!h]\n\\center\n\\includegraphics[width=1.0\\linewidth]{figs/figures_plot1.png}\n\\caption{Nice plot}\n\\end{figure}\n" -@test Weave.formatfigures(chunk, Weave.md2html) == "
\n\n
Nice plot
\n
\n" -@test Weave.formatfigures(chunk, Weave.rst) == ".. figure:: figs/figures_plot1.png\n :width: 100%\n\n Nice plot\n\n" -@test Weave.formatfigures(chunk, Weave.multimarkdown) == "![Nice plot][figs/figures_plot1.png]\n\n[figs/figures_plot1.png]: figs/figures_plot1.png width=100%\n" -@test Weave.formatfigures(chunk, Weave.adoc) == "image::figs/figures_plot1.png[width=100%,title=\"Nice plot\"]" +@test test_formatfigures(chunk, "tex") == "\\begin{figure}[!h]\n\\center\n\\includegraphics[width=1.0\\linewidth]{figs/figures_plot1.png}\n\\caption{Nice plot}\n\\end{figure}\n" +@test test_formatfigures(chunk, "pandoc") == "![Nice plot](figs/figures_plot1.png){width=100%}\n" +@test test_formatfigures(chunk, "md2tex") == "\\begin{figure}[!h]\n\\center\n\\includegraphics[width=1.0\\linewidth]{figs/figures_plot1.png}\n\\caption{Nice plot}\n\\end{figure}\n" +@test test_formatfigures(chunk, "md2html") == "
\n\n
Nice plot
\n
\n" +@test test_formatfigures(chunk, "rst") == ".. figure:: figs/figures_plot1.png\n :width: 100%\n\n Nice plot\n\n" +@test test_formatfigures(chunk, "multimarkdown") == "![Nice plot][figs/figures_plot1.png]\n\n[figs/figures_plot1.png]: figs/figures_plot1.png width=100%\n" +@test test_formatfigures(chunk, "asciidoc") == "image::figs/figures_plot1.png[width=100%,title=\"Nice plot\"]" + chunk.options[:label] = "somefig" -@test Weave.formatfigures(chunk, Weave.pandoc) == "![Nice plot](figs/figures_plot1.png){width=100% #fig:somefig}\n" -@test Weave.formatfigures(chunk, Weave.tex) == "\\begin{figure}[!h]\n\\center\n\\includegraphics[width=1.0\\linewidth]{figs/figures_plot1.png}\n\\caption{Nice plot}\n\\label{fig:somefig}\n\\end{figure}\n" -@test Weave.formatfigures(chunk, Weave.tex) == Weave.formatfigures(chunk, Weave.md2tex) +@test test_formatfigures(chunk, "pandoc") == "![Nice plot](figs/figures_plot1.png){width=100% #fig:somefig}\n" +@test test_formatfigures(chunk, "tex") == "\\begin{figure}[!h]\n\\center\n\\includegraphics[width=1.0\\linewidth]{figs/figures_plot1.png}\n\\caption{Nice plot}\n\\label{fig:somefig}\n\\end{figure}\n" + chunk.options[:label] = nothing chunk.options[:fig_cap] = nothing chunk.options[:fig_env] = "center" chunk.options[:fig_pos] = "" -@test Weave.formatfigures(chunk, Weave.tex) == "\\begin{center}\n\\includegraphics[width=1.0\\linewidth]{figs/figures_plot1.png}\n\\end{center}\n" +@test test_formatfigures(chunk, "tex") == "\\begin{center}\n\\includegraphics[width=1.0\\linewidth]{figs/figures_plot1.png}\n\\end{center}\n" + chunk.options[:out_width] = "50%" chunk.options[:out_height] = "75 %" -@test Weave.formatfigures(chunk, Weave.tex) == "\\begin{center}\n\\includegraphics[width=0.5\\linewidth,height=0.75\\paperheight]{figs/figures_plot1.png}\n\\end{center}\n" +@test test_formatfigures(chunk, "tex") == "\\begin{center}\n\\includegraphics[width=0.5\\linewidth,height=0.75\\paperheight]{figs/figures_plot1.png}\n\\end{center}\n" + chunk.options[:out_width] = "A%" chunk.options[:out_height] = "0.5\\textwidth" -@test Weave.formatfigures(chunk, Weave.tex) == "\\begin{center}\n\\includegraphics[width=A%,height=0.5\\textwidth]{figs/figures_plot1.png}\n\\end{center}\n" +@test test_formatfigures(chunk, "tex") == "\\begin{center}\n\\includegraphics[width=A%,height=0.5\\textwidth]{figs/figures_plot1.png}\n\\end{center}\n" diff --git a/test/formatter_test.jl b/test/formatter_test.jl index de4a379..5093752 100644 --- a/test/formatter_test.jl +++ b/test/formatter_test.jl @@ -11,11 +11,11 @@ Test rendering \$\alpha\$ dchunk = Weave.DocChunk(content, 1, 1) -pformat = Weave.formats["github"] +pformat = Weave.FORMATS["github"] f = Weave.format_chunk(dchunk, pformat) @test f == content -docformat = Weave.formats["md2html"] +docformat = Weave.FORMATS["md2html"] f_check = "

Test chunk

\n

Test rendering \$\alpha\$

\n" f = Weave.format_chunk(dchunk, docformat) @test f_check == f @@ -88,7 +88,7 @@ content = """ α """ chunk = Weave.DocChunk(content, 1, 1) -fmt = deepcopy(Weave.formats["md2tex"]) +fmt = deepcopy(Weave.FORMATS["md2tex"]) f = Weave.format_chunk(chunk, fmt) @test f == "\\section{Test chunk}\n\\ensuremath{\\alpha}\n\n"