diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 941d115..0a6d998 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: fail-fast: false # don't stop CI even when one of them fails matrix: version: - - '1.4' + - '1.5' os: - ubuntu-latest - macOS-latest diff --git a/.travis.yml b/.travis.yml index 2baf28a..cb33b33 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ language: julia jobs: include: - stage: "Documentation" - julia: 1 + julia: 1.5 os: linux script: - julia --project=doc/ -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate()' diff --git a/NEWS.md b/NEWS.md index 4742d7d..622559d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,19 @@ ## Release notes for Weave.jl -### v0.10 - 2020/05/18 +### v0.10.6 – 2020/10/03 + +improvements: +- cleaned up chunk rendering (removed unnecessary extra newlines): #401 +- `WEAVE_ARGS` now can take arbitrary objects: https://github.com/JunoLab/Weave.jl/commit/c24a2621359b5d0af1bb6825f488e58cc11b8a9e +- improved docs: #397 by @baggepinnen + +bug fixes +- fixed #398: #399 +- removed unnecessary quote for markdown output: https://github.com/JunoLab/Weave.jl/commit/a1830e05029f33195627ec5dedbacb30af23947e +- fixed #386: #396 by @torfjelde + + +### v0.10 – 2020/05/18 improvements: - `weave` is now integrated with Juno's progress bar; just call `weave` function inside Juno or use `julia-client: weave-to-html(pdf)` command (#331) diff --git a/Project.toml b/Project.toml index 46a710b..62ede36 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "Weave" uuid = "44d3d7a6-8a23-5bf8-98c5-b353f8df5ec9" -version = "0.10.2" +version = "0.10.6" [deps] Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" diff --git a/doc/src/header.md b/doc/src/header.md index cc86789..de1eb68 100644 --- a/doc/src/header.md +++ b/doc/src/header.md @@ -39,11 +39,11 @@ Note that `Date` is available since the chunk is evaluated first. --- title : Header Example author : Shuhei Kadowaki - date: `j Date(now())` + date: `j import Dates; Dates.Date(Dates.now())` --- ```julia; echo = false - using Datas + using Dates ``` ``` diff --git a/doc/src/usage.md b/doc/src/usage.md index a6cb589..36574cf 100644 --- a/doc/src/usage.md +++ b/doc/src/usage.md @@ -165,29 +165,12 @@ See [Header Configuration](@ref) section for more details. ## Passing Runtime Arguments to Documents -You can pass arguments as `Dict` to the weaved document using the `args` argument -to `weave`. The arguments will be available as `WEAVE_ARGS` variable in the document. +You can pass arbitrary object to the weaved document using [`weave`](@ref)'s optional argument `args`. +It will be available as `WEAVE_ARGS` variable in the `weave`d document. -This makes it possible to create the same report easily for e.g. different -date ranges of input data from a database or from files with similar format giving the -filename as input. +This makes it possible to create the same report easily for e.g. different date ranges of input data from a database or from files with similar format giving the filename as input. -In order to pass a filename to a document you need call `weave` using: - -```julia -weave("mydoc.jmd", args = Dict("filename" => "somedata.h5")) -``` - -and you can access the filename from document as follows: - -``` - ```julia - print(WEAVE_ARGS["filename"]) - ``` -``` - -You can use the `out_path` argument to control the name of the -output document. +E.g. if you call `weave("weavefile.jmd", args = (datalocation = "somedata.h5",))`, and then you can retrieve the `datalocation` in `weavefile.jmd` as follows: `WEAVE_ARGS.datalocation` ## `include_weave` diff --git a/src/Weave.jl b/src/Weave.jl index 898dabd..f740b4f 100644 --- a/src/Weave.jl +++ b/src/Weave.jl @@ -102,7 +102,7 @@ Weave an input document to output file. * `:doc`: Path of the source document (default) * `:pwd`: Julia working directory * `"somepath"`: `String` of output directory e.g. `"~/outdir"`, or of filename e.g. `"~/outdir/outfile.tex"` -- `args::Dict = Dict()`: Arguments to be passed to the weaved document; will be available as `WEAVE_ARGS` in the document +- `args::Any = Dict()`: A runtime object that is available as `WEAVE_ARGS` while `weave`ing - `mod::Union{Module,Nothing} = nothing`: Module where Weave `eval`s code. You can pass a `Module` object, otherwise create an new sandbox module. - `fig_path::Union{Nothing,AbstractString} = nothing`: Where figures will be generated, relative to `out_path`. By default (i.e. given `nothing`), Weave will automatically create `$(DEFAULT_FIG_PATH)` directory. - `fig_ext::Union{Nothing,AbstractString} = nothing`: Extension for saved figures e.g. `".pdf"`, `".png"`. Default setting depends on `doctype` @@ -127,7 +127,7 @@ function weave( doctype::Union{Nothing,AbstractString} = nothing, informat::Union{Nothing,AbstractString} = nothing, out_path::Union{Symbol,AbstractString} = :doc, - args::Dict = Dict(), + args::Any = Dict(), mod::Union{Module,Nothing} = nothing, fig_path::Union{Nothing,AbstractString} = nothing, fig_ext::Union{Nothing,AbstractString} = nothing, diff --git a/src/WeaveMarkdown/markdown.jl b/src/WeaveMarkdown/markdown.jl index d1c4c66..a0b70cc 100644 --- a/src/WeaveMarkdown/markdown.jl +++ b/src/WeaveMarkdown/markdown.jl @@ -5,23 +5,15 @@ using ..Weave: isnothing, take2string! using Markdown import Markdown: @trigger, @breaking, Code, MD, withstream, startswith, LaTeX - -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, - ) +# Note that this definition causes a "Method overwritten" warning, +# but defining this function in __init__() is not legal in julia v1.5 +function Markdown.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 mutable struct Comment diff --git a/src/plots.jl b/src/plots.jl index 9211c35..6746cb0 100644 --- a/src/plots.jl +++ b/src/plots.jl @@ -17,7 +17,7 @@ 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") @@ -39,7 +39,7 @@ function Base.display( report::Weave.Report, m::MIME"image/png", data::Plots.Plot{Plots.PlotlyBackend}, -)# +) display(report, MIME("image/svg+xml"), data) end diff --git a/src/rendering/common.jl b/src/rendering/common.jl index 8f39b03..b877809 100644 --- a/src/rendering/common.jl +++ b/src/rendering/common.jl @@ -44,18 +44,14 @@ function render_chunk(docformat::WeaveFormat, chunk::CodeChunk) chunk.content = render_code(docformat, chunk.content) - if !chunk.options[:eval] - return if chunk.options[:echo] - string(docformat.codestart, '\n', chunk.content, docformat.codeend) - else - "" - end - end + echo = chunk.options[:echo] + + chunk.options[:eval] || return echo ? string(docformat.codestart, chunk.content, docformat.codeend) : "" if chunk.options[:term] result = render_termchunk(docformat, chunk) else - result = if chunk.options[:echo] + result = if echo # Convert to output format and highlight (html, tex...) if needed string(docformat.codestart, chunk.content, docformat.codeend, '\n') else @@ -126,7 +122,7 @@ render_output(docformat::WeaveFormat, output) = output function render_termchunk(docformat::WeaveFormat, chunk) return if should_render(chunk) - string(docformat.termstart, chunk.output, '\n', docformat.termend, '\n') + string(docformat.termstart, chunk.output, docformat.termend) else "" end diff --git a/src/rendering/htmlformats.jl b/src/rendering/htmlformats.jl index d7854bd..054c00d 100644 --- a/src/rendering/htmlformats.jl +++ b/src/rendering/htmlformats.jl @@ -15,8 +15,8 @@ render_termchunk(docformat::HTMLFormat, chunk) = Base.@kwdef mutable struct WeaveHTML <: HTMLFormat description = "Weave-style HTML" extension = "html" - codestart = "\n" - codeend = "\n" + codestart = '\n' + codeend = '\n' termstart = codestart termend = codeend outputstart = "
"
diff --git a/src/rendering/miscformats.jl b/src/rendering/miscformats.jl
index 09ca0b4..6a19365 100644
--- a/src/rendering/miscformats.jl
+++ b/src/rendering/miscformats.jl
@@ -4,12 +4,12 @@
 Base.@kwdef mutable struct GitHubMarkdown <: WeaveFormat
     description = "GitHub Markdown"
     extension = "md"
-    codestart = "````julia"
-    codeend = "````\n\n"
+    codestart = "```julia"
+    codeend = "```\n"
     termstart = codestart
     termend = codeend
-    outputstart = "````"
-    outputend = "````\n\n"
+    outputstart = "```"
+    outputend = "```\n\n"
     fig_ext = ".png"
     mimetypes = ["image/png", "image/svg+xml", "image/jpg",
                 "text/markdown", "text/plain"]
@@ -50,12 +50,12 @@ end
 Base.@kwdef mutable struct Hugo <: WeaveFormat
     description = "Hugo Markdown (using shortcodes)"
     extension = "md"
-    codestart = "````julia"
-    codeend = "````\n\n"
+    codestart = "```julia"
+    codeend = "```\n"
     termstart = codestart
     termend = codeend
-    outputstart = "````"
-    outputend = "````\n\n"
+    outputstart = "```"
+    outputend = "```\n\n"
     mimetypes = default_mime_types
     fig_ext = ".png"
     out_width = nothing
@@ -88,12 +88,12 @@ end
 Base.@kwdef mutable struct MultiMarkdown <: WeaveFormat
     description = "MultiMarkdown"
     extension = "md"
-    codestart = "````julia"
-    codeend = "````\n\n"
+    codestart = "```julia"
+    codeend = "```\n"
     termstart = codestart
     termend = codeend
-    outputstart = "````"
-    outputend = "````\n\n"
+    outputstart = "```"
+    outputend = "```\n\n"
     mimetypes = default_mime_types
     fig_ext = ".png"
     out_width = nothing
@@ -143,7 +143,7 @@ Base.@kwdef mutable struct Rest <: WeaveFormat
     description = "reStructuredText and Sphinx"
     extension = "rst"
     codestart = ".. code-block:: julia\n"
-    codeend = "\n\n"
+    codeend = "\n"
     termstart = codestart
     termend = codeend
     outputstart = "::\n"
@@ -190,7 +190,7 @@ Base.@kwdef mutable struct AsciiDoc <: WeaveFormat
     description = "AsciiDoc"
     extension = "txt"
     codestart = "[source,julia]\n--------------------------------------"
-    codeend = "--------------------------------------\n\n"
+    codeend = "--------------------------------------\n"
     termstart = codestart
     termend = codeend
     outputstart = "--------------------------------------"
diff --git a/src/rendering/pandocformats.jl b/src/rendering/pandocformats.jl
index a01ceef..af71f0c 100644
--- a/src/rendering/pandocformats.jl
+++ b/src/rendering/pandocformats.jl
@@ -37,7 +37,7 @@ Base.@kwdef mutable struct Pandoc <: PandocFormat
     description = "Pandoc Markdown"
     extension = "md"
     codestart = "~~~~{.julia}"
-    codeend = "~~~~~~~~~~~~~\n\n"
+    codeend = "~~~~~~~~~~~~~\n"
     termstart = codestart
     termend = codeend
     outputstart = "~~~~"
diff --git a/src/run.jl b/src/run.jl
index 71442ed..b47aa97 100644
--- a/src/run.jl
+++ b/src/run.jl
@@ -7,7 +7,7 @@ function run_doc(
     doc::WeaveDoc;
     doctype::Union{Nothing,AbstractString} = nothing,
     out_path::Union{Symbol,AbstractString} = :doc,
-    args::Dict = Dict(),
+    args::Any = Dict(),
     mod::Union{Module,Nothing} = nothing,
     fig_path::Union{Nothing,AbstractString} = nothing,
     fig_ext::Union{Nothing,AbstractString} = nothing,
@@ -20,8 +20,9 @@ function run_doc(
     doc.format = deepcopy(get_format(doctype))
 
     cwd = doc.cwd = get_cwd(doc, out_path)
-    isdir(cwd) || mkdir(cwd)
+    mkpath(cwd)
 
+    # TODO: provide a way not to create `fig_path` ?
     if isnothing(fig_path)
         fig_path = if (endswith(doctype, "2pdf") && cache === :off) || endswith(doctype, "2html")
             basename(mktempdir(abspath(cwd)))
@@ -29,7 +30,7 @@ function run_doc(
             DEFAULT_FIG_PATH
         end
     end
-    let d = normpath(cwd, fig_path); isdir(d) || mkdir(d); end
+    mkpath(normpath(cwd, fig_path))
     # This is needed for latex and should work on all output formats
     @static Sys.iswindows() && (fig_path = replace(fig_path, "\\" => "/"))
     set_rc_params(doc, fig_path, fig_ext)
@@ -38,7 +39,7 @@ function run_doc(
 
     # New sandbox for each document with args exposed
     isnothing(mod) && (mod = sandbox = Core.eval(Main, :(module $(gensym(:WeaveSandBox)) end))::Module)
-    @eval mod WEAVE_ARGS = $args
+    Core.eval(mod, :(WEAVE_ARGS = $(args)))
 
     mimetypes = doc.format.mimetypes
 
@@ -139,27 +140,17 @@ function embed_figures!(chunk::CodeChunk, cwd)
         chunk.figures[i] = img2base64(fig, cwd)
     end
 end
-
-function embed_figures!(chunks::Vector{CodeChunk}, cwd)
-    for chunk in chunks
-        embed_figures!(chunk, cwd)
-    end
-end
+embed_figures!(chunks, cwd) = embed_figures!.(chunks, Ref(cwd))
 
 function img2base64(fig, cwd)
     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
+    return ext == ".png" ? "data:image/png;base64," * stringmime(MIME("image/png"), raw) :
+           ext == ".svg" ? "data:image/svg+xml;base64," * stringmime(MIME("image/svg"), raw) :
+           ext == ".gif" ? "data:image/gif;base64," * stringmime(MIME("image/gif"), raw) :
+           fig
 end
 
 function run_chunk(chunk::DocChunk, doc, report, mod)
@@ -225,7 +216,7 @@ function capture_output(code, mod, path, error, report)
     task_local_storage(:SOURCE_PATH, path) do
         try
             obj = include_string(mod, code, path) # TODO: fix line number
-            !isnothing(obj) && display(obj)
+            !isnothing(obj) && !REPL.ends_with_semicolon(code) && display(obj)
         catch _err
             err = unwrap_load_err(_err)
             error || throw(err)
@@ -273,15 +264,9 @@ function eval_chunk(doc::WeaveDoc, chunk::CodeChunk, report::Report, mod::Module
 
     execute_posthooks!(chunk)
 
-    chunks = if chunk.options[:term]
-        collect_term_results(chunk)
-    elseif chunk.options[:hold]
-        collect_hold_results(chunk)
-    else
-        collect_results(chunk)
-    end
-
-    return chunks
+    return chunk.options[:term] ? collect_term_results(chunk) :
+           chunk.options[:hold] ? collect_hold_results(chunk) :
+           collect_results(chunk)
 end
 
 # Hooks to run before and after chunks, this is form IJulia,
@@ -292,7 +277,11 @@ function pop_preexecution_hook!(f::Function)
     isnothing(i) && error("this function has not been registered in the pre-execution hook yet")
     return splice!(preexecution_hooks, i)
 end
-execute_prehooks!(chunk::CodeChunk) = for prehook in preexecution_hooks; Base.invokelatest(prehook, chunk); end
+function execute_prehooks!(chunk::CodeChunk)
+    for prehook in preexecution_hooks
+        Base.invokelatest(prehook, chunk)
+    end
+end
 
 const postexecution_hooks = Function[]
 push_postexecution_hook!(f::Function) = push!(postexecution_hooks, f)
@@ -301,7 +290,11 @@ function pop_postexecution_hook!(f::Function)
     isnothing(i) && error("this function has not been registered in the post-execution hook yet")
     return splice!(postexecution_hooks, i)
 end
-execute_posthooks!(chunk::CodeChunk) = for posthook in postexecution_hooks; Base.invokelatest(posthook, chunk); end
+function execute_posthooks!(chunk::CodeChunk)
+    for posthook in postexecution_hooks
+        Base.invokelatest(posthook, chunk)
+    end
+end
 
 """
     clear_module!(mod::Module)
@@ -340,11 +333,7 @@ function get_figname(report::Report, chunk; fignum = nothing, ext = nothing)
 end
 
 function set_rc_params(doc::WeaveDoc, fig_path, fig_ext)
-    if isnothing(fig_ext)
-        doc.chunk_defaults[:fig_ext] = doc.format.fig_ext
-    else
-        doc.chunk_defaults[:fig_ext] = fig_ext
-    end
+    doc.chunk_defaults[:fig_ext] = isnothing(fig_ext) ? doc.format.fig_ext : fig_ext
     doc.chunk_defaults[:fig_path] = fig_path
 end
 
@@ -352,11 +341,9 @@ function collect_results(chunk::CodeChunk)
     content = ""
     result_chunks = CodeChunk[]
     for r in chunk.result
+        content *= r.code
         # 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
+        if any(!isempty ∘ strip, (r.stdout, r.rich_output)) || !isempty(r.figures)
             rchunk = CodeChunk(
                 content,
                 chunk.number,
@@ -364,15 +351,14 @@ function collect_results(chunk::CodeChunk)
                 chunk.optionstring,
                 copy(chunk.options),
             )
-            content = ""
-            rchunk.figures = r.figures
             rchunk.output = r.stdout
             rchunk.rich_output = r.rich_output
+            rchunk.figures = r.figures
             push!(result_chunks, rchunk)
+            content = ""
         end
     end
     if !isempty(content)
-        startswith(content, "\n") || (content = "\n" * content)
         rchunk = CodeChunk(
             content,
             chunk.number,