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,