Compare commits

..

No commits in common. "master" and "v0.4.3" have entirely different histories.

147 changed files with 5721 additions and 4820 deletions

1
.github/FUNDING.yml vendored
View File

@ -1 +0,0 @@
custom: https://numfocus.salsalabs.org/donate-to-julia/index.html

View File

@ -1,31 +0,0 @@
---
name: bug report
about: create a bug report
title: "[BUG]"
labels: bug
assignees: ''
---
<!-- Please search existing issues to avoid duplicates. -->
## description
## versions
> `using InteractiveUtils; versioninfo()`:
<!-- please paste the the output of `using InteractiveUtils; versioninfo()` in the backticks below -->
```julia
```
> `using Pkg; Pkg.status()`:
<!-- please paste the the output of `using Pkg; Pkg.status()` in the backticks below -->
```julia
```
## minimum reproducible steps
<!-- if possible, paste here a minimum reproducible example document and steps -->

View File

@ -1,11 +0,0 @@
---
name: enhancement
about: suggest an enhancement idea
title: "[FR]"
labels: enhancement
assignees: ''
---
<!-- Please search existing issues to avoid duplicates. -->
## description

View File

@ -1,19 +0,0 @@
name: CompatHelper
on:
schedule:
- cron: '00 00 * * *'
jobs:
CompatHelper:
runs-on: ubuntu-latest
steps:
- uses: julia-actions/setup-julia@latest
with:
version: 1.3
- name: Pkg.add("CompatHelper")
run: julia -e 'using Pkg; Pkg.add("CompatHelper")'
- name: CompatHelper.main()
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: julia -e 'using CompatHelper; CompatHelper.main()'

View File

@ -1,11 +0,0 @@
name: TagBot
on:
schedule:
- cron: 0 * * * *
jobs:
TagBot:
runs-on: ubuntu-latest
steps:
- uses: JuliaRegistries/TagBot@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,67 +0,0 @@
# TODO: build docs via github-actions
name: CI
on:
push:
branches:
- master
pull_request:
jobs:
test:
name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }}
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.allow-failure }}
strategy:
fail-fast: false # don't stop CI even when one of them fails
matrix:
version:
- '1.5'
os:
- ubuntu-latest
- macOS-latest
# - windows-latest # TODO: test on Windows
arch:
- x64
- x86
allow-failure: [false]
include:
# this is so verbose... any other way to simplify this ?
- version: 'nightly'
os: ubuntu-latest
arch: x64
allow-failure: true
- version: 'nightly'
os: ubuntu-latest
arch: x86
allow-failure: true
- version: 'nightly'
os: macOS-latest
arch: x64
allow-failure: true
# - version: 'nightly'
# os: windows-latest
# arch: x64
# allow-failure: true
# - version: 'nightly'
# os: windows-latest
# arch: x86
# allow-failure: true
exclude:
- os: macOS-latest
arch: x86
steps:
- uses: actions/checkout@v2
- uses: julia-actions/setup-julia@v1
with:
version: ${{ matrix.version }}
arch: ${{ matrix.arch }}
- uses: julia-actions/julia-buildpkg@latest
- uses: julia-actions/julia-runtest@latest
- uses: julia-actions/julia-processcoverage@v1
- uses: codecov/codecov-action@v1
with:
file: ./lcov.info
flags: unittests
name: codecov-umbrella
fail_ci_if_error: false
token: ${{ secrets.CODECOV_TOKEN }}

8
.gitignore vendored
View File

@ -1,5 +1,6 @@
Manifest.toml
src/*.cov
test.jl
examples/figures/
examples/*.md
examples/*.pdf
@ -16,14 +17,17 @@ test/**/*.html
test/**/*.pdf
test/**/*.png
test/**/chunk_options.jl
test/**/*.ipynb
!test/**/*ref.*
doc/build
doc/site
stable/
tmp/
.idea
*.*~
*.aux
*.log
*.out
\#*\#
.juliahistory

View File

@ -1,14 +1,26 @@
language: julia
jobs:
include:
- stage: "Documentation"
julia: 1.5
os: linux
script:
- julia --project=doc/ -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate()'
- julia --project=doc/ doc/make.jl
after_success: skip
julia:
- 0.5
- 0.6
- nightly
matrix:
allow_failures:
- julia: nightly
notifications:
email: false
before_install:
#- sudo apt-get update -qq -y
#- sudo apt-get install python-matplotlib -y
- wget http://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh -O miniconda.sh
- chmod +x miniconda.sh
- ./miniconda.sh -b
- export PATH=/home/travis/miniconda2/bin:$PATH
- conda update --yes conda
- conda install --yes matplotlib
script:
- if [[ -a .git/shallow ]]; then git fetch --unshallow; fi
- julia --check-bounds=yes -e 'Pkg.clone(pwd()); Pkg.build("Weave")'
- xvfb-run julia -e 'Pkg.test("Weave", coverage=true)'
after_success:
- julia -e 'cd(Pkg.dir("Weave")); Pkg.add("Coverage"); using Coverage; Coveralls.submit(Coveralls.process_folder())'

View File

@ -1,20 +1,22 @@
Copyright (c) 2020: Contributors
The Weave.jl package is licensed under the MIT "Expat" License:
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
> Copyright (c) 2014-2017: Matti Pastell.
>
> Permission is hereby granted, free of charge, to any person obtaining
> a copy of this software and associated documentation files (the
> "Software"), to deal in the Software without restriction, including
> without limitation the rights to use, copy, modify, merge, publish,
> distribute, sublicense, and/or sell copies of the Software, and to
> permit persons to whom the Software is furnished to do so, subject to
> the following conditions:
>
> The above copyright notice and this permission notice shall be
> included in all copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
> IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
> CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
> TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
> SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

68
NEWS.md
View File

@ -1,66 +1,20 @@
## Release notes for Weave.jl
### 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)
- document metadata in YAML header can be given dynamically (#329)
- headers are now striped more gracefully; only Weave.jl related header is stripped when weaving to `github` or `hugo` document (#329, #305)
- `WeavePlots`/`GadflyPlots` won't be loaded into `Main` module (#322)
- un`const` bindings in a sandbox module are correctly cleared, helping GC free as much memory usage as possible (#317)
- keep latex figures even if weaving failed (#302)
- bunch of documentation improvements (#297, #295)
- code size in HTML header is now not hardcoded, leading to more readable font size (#281)
bug fixes:
- display of "big" object is fixed and limited (#311)
- fix dependencies issues
internal:
- bunch of internal refactors, code clean up (#330, #327, #325, #321, #320, #319, #318, #313)
- internal error now doesn't mess up display system (#316)
- format code base (#312)
breaking change:
- `options` YAML key is deprecated, use `weave_options` key instead (#334)
- `set_chunk_defaults` is now renamed to `set_chunk_defaults!` (#323)
- `restore_chunk_defaults` is now renamed to `restore_chunk_defaults!` (#323)
---
# Release notes for Weave.jl
### v0.4.1
* Disable precompilation due to warnings from dependencies
* Disable precompilation due to warnings from depencies
* Fix deprecation warnings for Julia 0.6
* Fix PyPlot for Julia 0.6
* Support citations in `pandoc2html` and `pandoc2pdf` output
* Fix extra whitespace when `term=true`
* Fix mime type priority for `md2html`
### V0.4.0
* Support passing arguments to document using `args` option
* Add `include_weave` for including code from Weave documents
* Add support for inline code chunks
* Remove generated figure files when publishing to html and pdf
### v0.3.0
* Add support for YAML title block
@ -71,13 +25,11 @@ breaking change:
* Fix extra whitespace from code chunk output
* Improved GR and GLVisualize support with Plots
### v0.2.2
* Add IJulia notebook as input format
* Add `convert_doc` method to convert between input formats
### v0.2.1
* Fix critical hanging on Windows using proper handling of redirect_stdout
@ -85,7 +37,6 @@ breaking change:
output in published HTML documents.
* Fix semicolons for `term=true`
### v0.2
* Move to Julia 0.5 only
@ -106,7 +57,6 @@ breaking change:
- Fix parsing of lone variables from chunks
- Fix error with md2html formatter and dates #38
### v0.1.2
27th April 2016
@ -116,7 +66,6 @@ breaking change:
* Improve doctype autodetection
* Improved regex for parsing markdown input format
### v0.1.1
* Change pandoc output to use inline images if there is no caption.
@ -132,7 +81,6 @@ breaking change:
* Autodetect input and output formats based on filename
* Allow `out_path` be a file or directory.
### v0.1.0
19th April 2016
@ -149,7 +97,6 @@ breaking change:
- Chunks are now represented with their own type. Allows multiple dispatch
and easier implementation of new chunks.
### 0.0.4
4th January 2015
@ -159,7 +106,6 @@ breaking change:
* New option: `out_path` for controlling where weaved documents and figures are saved
* Command line script `bin/weave.jl` for calling weave from command line
### 0.0.3
9th December 2014
@ -175,8 +121,8 @@ breaking change:
7th December 2014
First release:
- Noweb and markdown input formats
- Support for Gadfly, Winston and PyPlot figures
- Term and script chunks
- Support for markdown, tex and rst output
* First release
* Noweb and markdown input formats
* Support for Gadfly, Winston and PyPlot figures
* Term and script chunks
* Support for markdown, tex and rst output

View File

@ -1,37 +0,0 @@
name = "Weave"
uuid = "44d3d7a6-8a23-5bf8-98c5-b353f8df5ec9"
version = "0.10.12"
[deps]
Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
Highlights = "eafb193a-b7ab-5a9e-9068-77385905fa72"
JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a"
Mustache = "ffc61752-8dc7-55ee-8c37-f3e9cdd09e70"
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"
RelocatableFolders = "05181044-ff0b-4ac5-8273-598c1e38db00"
Requires = "ae029012-a4dd-5104-9daa-d747884805df"
Serialization = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
YAML = "ddb6d928-2868-570f-bddf-ab3f9cf99eb6"
[compat]
Highlights = "0.3.1, 0.4, 0.5"
JSON = "0.21"
Mustache = "0.4.1, 0.5, 1"
Plots = "0.28, 0.29, 1.0"
RelocatableFolders = "0.1,0.2,0.3,1"
Requires = "1.0"
YAML = "0.3, 0.4"
julia = "1.2"
[extras]
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
Gadfly = "c91e804a-d5a3-530f-b6f0-dfbca275c004"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
[targets]
test = ["DataFrames", "Test"]

105
README.md
View File

@ -1,104 +1,93 @@
# Weave
![CI](https://github.com/JunoLab/Weave.jl/workflows/CI/badge.svg)
[![codecov](https://codecov.io/gh/JunoLab/Weave.jl/branch/master/graph/badge.svg)](https://codecov.io/gh/JunoLab/Weave.jl)
[![](https://img.shields.io/badge/docs-stable-blue.svg)](http://weavejl.mpastell.com/stable/)
[![](https://img.shields.io/badge/docs-dev-blue.svg)](http://weavejl.mpastell.com/dev/)
[![Build Status](https://travis-ci.org/mpastell/Weave.jl.svg?branch=master)](https://travis-ci.org/mpastell/Weave.jl)
[![Build status](https://ci.appveyor.com/api/projects/status/r97pwi9x8ard6xk6/branch/master?svg=true)](https://ci.appveyor.com/project/mpastell/weave-jl/branch/master)
[![Coverage Status](https://img.shields.io/coveralls/mpastell/Weave.jl.svg)](https://coveralls.io/r/mpastell/Weave.jl?branch=master)
[![](https://img.shields.io/badge/docs-stable-blue.svg)](https://mpastell.github.io/Weave.jl/stable)
[![](http://joss.theoj.org/papers/10.21105/joss.00204/status.svg)](http://dx.doi.org/10.21105/joss.00204)
Weave is a scientific report generator/literate programming tool for the [Julia programming language](https://julialang.org/).
It resembles
[Pweave](http://mpastell.com/pweave),
[knitr](https://yihui.org/knitr/),
[R Markdown](https://rmarkdown.rstudio.com/),
and [Sweave](https://stat.ethz.ch/R-manual/R-patched/library/utils/doc/Sweave.pdf).
Weave is a scientific report generator/literate programming tool
for Julia. It resembles [Pweave](http://mpastell.com/pweave), Knitr, rmarkdown
and Sweave.
You can write your documentation and code in input document using Markdown, Noweb or ordinal Julia script syntax,
and then use `weave` function to execute code and generate an output document while capturing results and figures.
You can write your documentation and code in input document using Noweb,
Markdown, Script syntax and use `weave` function to execute to document to capture results
and figures.
**Current features**
- Publish markdown directly to HTML and PDF using Julia or [Pandoc](https://pandoc.org/MANUAL.html)
- Execute code as in terminal or in a unit of code chunk
- Capture [Plots.jl](https://github.com/JuliaPlots/Plots.jl) or [Gadfly.jl](https://github.com/GiovineItalia/Gadfly.jl) figures
- Supports various input format: Markdown, [Noweb](https://www.cs.tufts.edu/~nr/noweb/), [Jupyter Notebook](https://jupyter.org/), and ordinal Julia script
- Conversions between those input formats
- Supports various output document formats: HTML, PDF, GitHub markdown, Jupyter Notebook, MultiMarkdown, Asciidoc and reStructuredText
- Simple caching of results
* Noweb, markdown or script syntax for input documents.
* Execute code as terminal or "script" chunks.
* Capture Plots, Gadfly and PyPlot figures.
* Supports LaTex, Pandoc, Github markdown, MultiMarkdown, Asciidoc and reStructuredText output
* Publish markdown directly to html and pdf using Julia or Pandoc markdown.
* Simple caching of results
* Convert to and from IJulia notebooks
**Citing Weave:** *Pastell, Matti. 2017. Weave.jl: Scientific Reports Using Julia. The Journal of Open Source Software. http://dx.doi.org/10.21105/joss.00204*
![Weave in Juno demo](https://user-images.githubusercontent.com/40514306/76081328-32f41900-5fec-11ea-958a-375f77f642a2.png)
**Note about Julia 0.6**: Weave has been updated to support 0.6, but most of the depencies and plotting libraries have not. Plotting currently (14th March 2017) only works using PyPlot master.
![Weave code and output](http://mpastell.com/images/weave_demo.png)
## Installation
You can install the latest release using Julia package manager:
```julia
using Pkg
Pkg.add("Weave")
```
## Usage
Run from julia using Gadfly for plots:
```julia
#First add depencies for the example
Pkg.add.(["Cairo", "Fontconfig", "Gadfly"])
#Use Weave
using Weave
# add depencies for the example
using Pkg; Pkg.add(["Plots", "DSP"])
filename = normpath(Weave.EXAMPLE_FOLDER, "FIR_design.jmd")
weave(filename, out_path = :pwd)
weave(Pkg.dir("Weave","examples","gadfly_sample.mdw"))
```
If you have LaTeX installed you can also weave directly to pdf.
If you have Pandoc installed you can also weave directly to html and pdf.
```julia
filename = normpath(Weave.EXAMPLE_FOLDER, "FIR_design.jmd")
weave(filename, out_path = :pwd, doctype = "md2pdf")
weave(Pkg.dir("Weave","examples","gadfly_md_sample.jmd"), informat="markdown",
out_path = :pwd, doctype = "md2html")
```
NOTE: `Weave.EXAMPLE_FOLDER` just points to [`examples` directory](./examples).
## Documentation
Documenter.jl with MKDocs generated documentation:
[![](https://img.shields.io/badge/docs-stable-blue.svg)](http://weavejl.mpastell.com/stable/)
[![](https://img.shields.io/badge/docs-dev-blue.svg)](http://weavejl.mpastell.com/dev/)
[![](https://img.shields.io/badge/docs-stable-blue.svg)](https://mpastell.github.io/Weave.jl/stable)
[![](https://img.shields.io/badge/docs-latest-blue.svg)](https://mpastell.github.io/Weave.jl/latest)
## Editor support
Install [language-weave](https://atom.io/packages/language-weave) to add Weave support to Juno.
It allows running code from Weave documents with usual keybindings and allows preview of
html and pdf output.
I have made [language-weave](https://atom.io/packages/language-weave) package
for Atom to do the syntax highlighting correctly.
The [Julia extension for Visual Studio Code](https://www.julia-vscode.org/)
adds Weave support to [Visual Studio Code](https://code.visualstudio.com/).
Noweb documents work well with ESS as well, to set doc-mode for .mdw files to markdown
and code to Julia you can do:
```clojure
(defun mdw-mode ()
(ess-noweb-mode)
(setq ess-noweb-default-code-mode 'ess-julia-mode)
(setq ess-noweb-doc-mode 'markdown-mode))
(setq auto-mode-alist (append (list (cons "\\.mdw$" 'mdw-mode))
auto-mode-alist))
```
## Contributing
You can contribute to this package by opening issues on GitHub or implementing things yourself and making a pull request.
We'd also appreciate more example documents written using Weave.
I will probably add new features to Weave when I need them myself or if they are requested and not too difficult to implement. You can contribute by opening issues on Github or implementing things yourself and making a pull request. I'd also appreciate example documents written using Weave to add to examples.
## Contributors
You can see the list of contributors on GitHub: https://github.com/JunoLab/Weave.jl/graphs/contributors .
Thanks for the important additions, fixes and comments.
## Example projects using Weave
- [DiffEqTutorials.jl](https://github.com/JuliaDiffEq/DiffEqTutorials.jl) uses Weave to output tutorials (`.jmd` documents) to html, pdf and Jupyter notebooks.
- [TuringTutorials](https://github.com/TuringLang/TuringTutorials) uses Weave to convert notebooks to html.
## Related packages
- [Literate.jl](https://github.com/fredrikekre/Literate.jl) can be used to generate Markdown and Jupyter notebooks directly from Julia source files with markdown in comments.
- [Quarto](https://quarto.org) can generate Jupyter notebooks, HTML, or PDF directly from a Markdown format containing Julia code blocks, and also works with R and Python.
You can see the list of contributors on Github: https://github.com/mpastell/Weave.jl/graphs/contributors. Thanks for the important additions, fixes and comments.

9
REQUIRE Normal file
View File

@ -0,0 +1,9 @@
julia 0.5
ArgParse
JLD
JSON
Highlights
Mustache
Documenter
YAML
Compat 0.17.0

31
appveyor.yml Normal file
View File

@ -0,0 +1,31 @@
environment:
matrix:
- JULIAVERSION: "julialang/bin/winnt/x64/0.5/julia-0.5-latest-win64.exe"
branches:
only:
- master
- /release-.*/
notifications:
- provider: Email
on_build_success: false
on_build_failure: false
on_build_status_changed: false
install:
# Download most recent Julia Windows binary
- ps: (new-object net.webclient).DownloadFile(
$("http://s3.amazonaws.com/"+$env:JULIAVERSION),
"C:\projects\julia-binary.exe")
# Run installer silently, output to C:\projects\julia
- C:\projects\julia-binary.exe /S /D=C:\projects\julia
build_script:
# Need to convert from shallow to complete for Pkg.clone to work
- IF EXIST .git\shallow (git fetch --unshallow)
- C:\projects\julia\bin\julia -e "ENV[\"PYTHON\"]=\"\"; Pkg.add(\"PyPlot\")"
- C:\projects\julia\bin\julia -e "Pkg.clone(pwd(), \"Weave\"); versioninfo(); Pkg.build(\"Weave\")"
test_script:
- C:\projects\julia\bin\julia --check-bounds=yes -e "Pkg.test(\"Weave\")"

View File

@ -14,14 +14,14 @@ ap = ArgParseSettings("Weave Julia documents using Weave.jl",
help = "source document(s)"
required = true
"--doctype"
default = nothing
default = :auto
help = "output format"
"--plotlib"
arg_type = String
default = "Gadfly"
help = "output format"
"--informat"
default = nothing
default = :auto
help = "output format"
"--out_path"
arg_type = String
@ -41,8 +41,12 @@ source = args["source"]
delete!(args, "source")
args_col = []
args["plotlib"] == "nothing" && (args["plotlib"] = nothing)
#Check for special values of out_path
#args["informat"] == ":auto" && (args["informat"] = :auto)
#args["doctype"] == ":auto" && (args["informat"] = :auto)
if args["out_path"] == ":doc"
args["out_path"] = :doc
elseif args["out_path"] == ":pwd"
@ -50,7 +54,7 @@ elseif args["out_path"] == ":pwd"
end
for (key, val) in args
push!(args_col, (Meta.parse(key), val))
push!(args_col, (parse(key), val))
end
for s=source

View File

@ -1,4 +0,0 @@
[deps]
DSP = "717857b8-e6f2-59f4-9121-6e50c889abd2"
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"

Binary file not shown.

Binary file not shown.

View File

@ -1,29 +1,3 @@
using Documenter, Weave
CI_FLG = get(ENV, "CI", nothing) == "true"
makedocs(
modules = [Weave],
format = Documenter.HTML(
prettyurls = CI_FLG,
canonical = "http://weavejl.mpastell.com/stable/",
),
sitename = "Weave.jl",
pages = [
"index.md",
"getting_started.md",
"usage.md",
"publish.md",
"chunk_options.md",
"header.md",
"notebooks.md",
"function_index.md",
],
)
CI_FLG && include("make_examples.jl")
deploydocs(
repo = "github.com/JunoLab/Weave.jl.git",
push_preview = true,
)
makedocs(modules = Weave)

View File

@ -1,30 +1,21 @@
using Weave
let start_dir = pwd()
cd(@__DIR__)
weave("../examples/gadfly_md_sample.jmd",
informat="markdown", out_path = "build/examples", doctype = "pandoc")
weave("../examples/FIR_design.jmd", doctype = "pandoc", out_path = "build/examples")
weave("../examples/FIR_design.jmd", doctype = "md2html", out_path = "build/examples")
weave("../examples/FIR_design_plots.jl", doctype = "md2html", out_path = "build/examples")
weave("../examples/gadfly_md_sample.jmd",
informat="markdown", out_path = "build/examples", doctype = "md2html")
# PDF outputs
if haskey(ENV, "TRAVIS")
# in Travis, just cp already generated PDFs
cp("assets/FIR_design.pdf", "build/examples/FIR_design.pdf", force = true)
cp("assets/FIR_design_plots.pdf", "build/examples/FIR_design_plots.pdf", force = true)
else
# otherwise try to generate them
try
weave("../examples/FIR_design.jmd", doctype = "md2pdf", out_path = "assets")
weave("../examples/FIR_design_plots.jl", doctype = "md2pdf", out_path = "assets")
catch err
@error err
end
end
weave("../examples/gadfly_md_sample.jmd",
informat="markdown", out_path = "build/examples", doctype = "md2pdf")
cp("../examples/FIR_design.jmd", "build/examples/FIR_design.jmd", force = true)
cp("build/examples/FIR_design.md", "build/examples/FIR_design.txt", force = true)
cp("../examples/FIR_design_plots.jl", "build/examples/FIR_design_plots.jl", force = true)
cp("../examples/gadfly_md_sample.jmd",
"build/examples/gadfly_md_sample.jmd", remove_destination = true)
cd(start_dir)
end
cp("build/examples/gadfly_md_sample.md",
"build/examples/gadfly_md_sample.txt", remove_destination = true)
weave("../examples/FIR_design.jl", out_path = "build/examples")
weave("../examples/FIR_design.jl", doctype = "md2pdf", out_path = "build/examples")
cp("../examples/FIR_design.jl",
"build/examples/FIR_design.jl", remove_destination = true)

45
doc/mkdocs.yml Normal file
View File

@ -0,0 +1,45 @@
site_name: Weave.jl
site_author: Matti Pastell
copyright: Matti Pastell 2016
repo_url: https://github.com/mpastell/Weave.jl
theme: material
#extra:
# palette:
# primary: 'indigo'
# accent: 'blue'
extra_css:
- assets/Documenter.css
#extra_javascript:
# - https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML
# - assets/mathjaxhelper.js
markdown_extensions:
- extra
- tables
- fenced_code
- codehilite(css_class=code)
# - mdx_math
google_analytics:
- 'UA-6474352-10'
- 'auto'
extra:
version: 'v0.4.1'
author:
twitter: 'mpastell'
docs_dir: 'build'
pages:
- Home: index.md
- Getting started: getting_started.md
- Using Weave: usage.md
- Publishing scripts: publish.md
- Chunk options: chunk_options.md
- Working with Jupyter notebooks: notebooks.md
- Function index: function_index.md

View File

@ -1,81 +1,64 @@
# [Chunk Options](@id chunk-options)
# Chunk options
You can use chunk options to configure how each chunk is evaluated, rendered, etc.
Most of the ideas came from [chunk options in RMarkdown](http://yihui.name/knitr/options).
I've mostly followed [Knitr](http://yihui.name/knitr/options)'s naming for chunk options, but not all options are implemented.
Options are separated using ";" and need to be valid Julia expressions. Example: A code chunk that saves and displays a 12 cm wide image and hides the source code:
## Syntax
Chunk options come after [code chunk](@ref code-chunks) header.
There are two (slightly) different syntax to write them:
- (Julia's toplevel expression) options are separated by semicolon (`;`)
- (RMarkdown style) options are separated by comma (`,`)
Let's take a look at examples. All the following code chunk header are valid,
and so configured to hide the source code from generated output (`echo = false`)
and displays figures with 12cm width (`out_width = "12cm"`):
```md
```julia; echo = false; out_width = "12cm"
```{julia; echo = false; out_width = "12cm"}
```julia, echo = false, out_width = "12cm"
```{julia, echo = false, out_width = "12cm"}
```julia
<<fig_width=5; echo=false >>=
using Gadfly
x = linspace(0, 2π, 200)
plot(x=x, y = sin(x), Geom.line)
@
```
Weave currently supports the following chunk options with the following defaults:
## Weave Chunk Options
## Options for code
Weave currently supports the following chunk options:
we've mostly followed [RMarkdown's namings](http://yihui.name/knitr/options), but not all options are implemented.
* `echo = true`. Echo the code in the output document. If `false` the source code will be hidden.
* `results = "markup"`. The output format of the printed results. "markup" for literal block, "hidden" for hidden results or anything else for raw output (I tend to use tex for Latex and rst for rest. Raw output is useful if you want to e.g. create tables from code chunks.
* `eval = true`. Evaluate the code chunk. If false the chunk wont be executed.
* `term=false`. If true the output emulates a REPL session. Otherwise only stdout and figures will be included in output.
* `label`. Chunk label, will be used for figure labels in Latex as fig:label
* `wrap = true`. Wrap long lines from output.
* `line_width = 75`. Line width for wrapped lines.
* `cache = false`. Cache results, depends on `cache` parameter on `weave` function.
* `hold = false`. Hold all results until the end of the chunk.
* `tangle = true`. Set tangle to false to exclude chunk from tangled code.
### Evaluation
## Options for figures
- `eval = true`: Evaluate the code chunk. If `false` the chunk wont be executed.
- `error = true`: If `true` [`weave`](@ref) won't stop on errors and rather they will be included in output document. If `false`, [`weave`](@ref) will halt on any of un-caught errors.
- `cache = false`: Cache results, depending on `cache` parameter on [`weave`](@ref) function.
- `tangle = true`: Set tangle to `false` to exclude chunk from tangled code.
### Rendering
- `echo = true`: Echo the code in the output document. If `false` the source code will be hidden.
- `results = "markup"`: The output format of the printed results. `"markup"` for literal block, `"hidden"` for hidden results, or anything else for raw output (I tend to use `"tex"` for Latex and `"rst"` for rest). Raw output is useful if you want to e.g. create tables from code chunks.
- `term = false`: If `true` the output emulates a REPL session. Otherwise only stdout and figures will be included in output.
- `wrap = true`: Wrap long lines from output.
- `line_width = 75`: Line width for wrapped lines.
- `hold = false`: Hold all results until the end of the chunk.
### Figures
- `label = nothing`: Chunk label, will be used for figure labels in Latex as `fig:label`.
- `fig_width = 6`: Figure width passed to plotting library.
- `fig_height = 4`: Figure height passed to plotting library.
- `out_width`: Width of saved figure in output markup e.g. `"50%"`, `"12cm"`, `0.5\linewidth`
- `out_height`: Height of saved figure in output markup
- `dpi = 96`: Resolution of saved figures.
- `fig_cap`: Figure caption.
- `fig_ext`: File extension (format) of saved figures.
- `fig_pos = "!h"`: Figure position in Latex, e.g.: `"ht"`.
- `fig_env = "figure"`: Figure environment in Latex.
* `fig_width`. Figure width defined in markup, default depends on the output format.
* `out_width`. Width of saved figure.
* `out_height`. Height of saved figure.
* `dpi`=96. Resolution of saved figures.
* `fig_cap`. Figure caption.
* `label`. Chunk label, will be used for figure labels in Latex as fig:label
* `fig_ext`. File extension (format) of saved figures.
* `fig_pos="htpb"`. Figure position in Latex.
* `fig_env="figure"`. Figure environment in Latex.
## Default Chunk Options
## Set default chunk options
You can set the default chunk options (and `weave` arguments) for a document using `weave_options` key in YAML [Header Configuration](@ref).
E.g. to set the default `out_width` of all figures you can use:
You can set or alter the default chunk options for a document either before
running weave or inside the weaved document. You can e.g. use a hidden chunk
in the beginning of the source document to set the options:
```yaml
---
weave_options:
out_width : 50%
---
```julia
<<echo = false>>=
import Weave
Weave.set_chunk_defaults(Dict{Symbol, Any}(
:out_width => "\\0.5linewidth",
:results => "tex"
))
@
```
You can also set or change the default chunk options for a document either before weave using the `set_chunk_defaults` function.
```@docs
set_chunk_defaults!
get_chunk_defaults
restore_chunk_defaults!
set_chunk_defaults(opts)
get_chunk_defaults()
restore_chunk_defaults()
```

View File

@ -1,41 +1,36 @@
# Getting started
The best way to get started using Weave.jl is to look at the example input and output documents.
Examples for different formats are included in the package's [`examples`](https://github.com/JunoLab/Weave.jl/tree/master/examples) directory.
The best way to get started using Weave.jl is to look at the example input and
output documents. Examples for different formats are included in the packages `examples` directory.
First have a look at source document using markdown code chunks and [Plots.jl](https://github.com/JuliaPlots/Plots.jl) for figures:
First have a look at source document using markdown code chunks and Gadfly for
figures: [gadfly_md_sample.jmd](examples/gadfly_md_sample.jmd) and then see the
output in different formats:
All the different format documents below are generated from a single Weave document [`FIR_design.jmd`](../examples/FIR_design.jmd):
- HTML: [`FIR_design.html`](../examples/FIR_design.html)
- PDF: [`FIR_design.pdf`](../examples/FIR_design.pdf)
- Pandoc markdown: [`FIR_design.txt`](../examples/FIR_design.txt)
- Pandoc markdown: [gadfly_md_sample.md](examples/gadfly_md_sample.txt)
- HTML: [gadfly_md_sample.html](examples/gadfly_md_sample.html)
- pdf: [gadfly_md_sample.pdf](examples/gadfly_md_sample.pdf)
!!! note
Producing PDF output requires that you have XeLateX installed.
*Producing pdf output requires that you have pdflatex installed.*
Add dependencies for the example if needed:
Add depencies for the example if needed:
```julia
using Pkg; Pkg.add.(["Plots", "DSP"])
Pkg.add.(["Cairo", "Fontconfig", "Gadfly"])
```
Weave the files to your working directory:
Weave the files to your working directory using:
```julia
using Weave
filename = normpath(Weave.EXAMPLE_FOLDER, "FIR_design.jmd")
# Julia markdown to HTML
weave(filename; doctype = "md2html", out_path = :pwd)
# Julia markdown to PDF
weave(filename; doctype = "md2pdf", out_path = :pwd)
# Julia markdown to Pandoc markdown
weave(filename; doctype = "pandoc", out_path = :pwd)
#Markdown
weave(Pkg.dir("Weave","examples","gadfly_md_sample.jmd"), out_path = :pwd,
doctype = "pandoc")
#HTML
weave(Pkg.dir("Weave","examples","gadfly_md_sample.jmd"), out_path = :pwd,
doctype = "md2html")
#pdf
weave(Pkg.dir("Weave","examples","gadfly_md_sample.jmd"), out_path = :pwd,
doctype = "md2pdf")
```
!!! tips
`Weave.EXAMPLE_FOLDER` points to [the `examples` directory](https://github.com/JunoLab/Weave.jl/tree/master/examples).

View File

@ -1,101 +0,0 @@
# Header Configuration
When `weave`ing a markdown document, you use YAML header to provide additional metadata and configuration options.
A YAML header should be in the beginning of the input document delimited with `---`.
!!! warning
YAML header configuration is only supported when `weave`ing [markdown or Noweb syntax documents](@ref document-syntax).
## Document Metadata
You can set additional document metadata in YAML header.
When `weave`ing to Julia markdown documents to HTML or PDF, Weave respects the following metadata specification:
- `title`
- `author`
- `date`
An example:
```yaml
---
title : Header Example
author : Shuhei Kadowaki
date: 16th May 2020
---
```
!!! note
You can also have other metadata, but they won't appear in the resulting HTML and PDF.
If you weave to Julia markdown to GitHub/Hugo markdown, all the metadata will be preserved.
### Dynamic Metadata
The metadata can be given "dynamically"; if you have [inline code](@ref) within YAML header, they will be evaluated _after_ evaluating all the chunks and replaced with the results.
The example document below will set `date` metadata dynamically.
Note that `Date` is available since the chunk is evaluated first.
```md
---
title : Header Example
author : Shuhei Kadowaki
date: `j import Dates; Dates.Date(Dates.now())`
---
```julia; echo = false
using Dates
```
```
## Configuration Options
Each of keyword arguments of [`weave`](@ref) can be set in the YAML header under `options` field.
You can also set [Chunks Options](@ref) there that will be applied globally.
The example below sets `out_path` and `doctype` options and overwrites `term` and `wrap` chunk options:
```yaml
---
title : Header Example
author : Shuhei Kadowaki
date: 16th May 2020
weave_options:
out_path: relative/path/to/this/document
doctype: github
term: true
wrap: false
---
```
!!! note
- configurations specified within the YAML header have higher precedence than those specified via `weave` keyword arguments
- chunk options specified within each chunk have higher precedence than the global global chunk options specified within the YAML header
!!! warning
As opposed to metadata, _most_ of those configuration options can't be given dynamically (i.e. can't be via inline code),
since they are needed for evaluation of chunks themselves.
But some configuration options that are needed "formatting" document can still be given dynamically:
- `template`
- `css`
- `highlight_theme`
- `pandoc_options`
- `latex_cmd`
- `keep_unicode`
See also: [`weave`](@ref)
## Format Specific Options
The header configurations can be format specific.
Here is how to set different `out_path` for `md2html` and `md2pdf` and set `fig_ext` globally:
```yaml
---
weave_options:
md2html:
out_path : html
md2pdf:
out_path : pdf
fig_ext : .png
---
```

View File

@ -1,38 +1,27 @@
# Weave.jl - Scientific Reports Using Julia
This is the documentation of [Weave.jl](http://github.com/mpastell/weave.jl).
Weave is a scientific report generator/literate programming tool for Julia.
It resembles
[Pweave](http://mpastell.com/pweave),
[knitr](https://yihui.org/knitr/),
[R Markdown](https://rmarkdown.rstudio.com/),
and [Sweave](https://stat.ethz.ch/R-manual/R-patched/library/utils/doc/Sweave.pdf).
This is the documentation of [Weave.jl](http://github.com/mpastell/weave.jl). Weave is a scientific report generator/literate programming tool
for Julia. It resembles [Pweave](http://mpastell.com/pweave), Knitr, rmarkdown
and Sweave.
**Current features**
- Publish markdown directly to HTML and PDF using Julia or [Pandoc](https://pandoc.org/MANUAL.html)
- Execute code as in terminal or in a unit of code chunk
- Capture [Plots.jl](https://github.com/JuliaPlots/Plots.jl) or [Gadfly.jl](https://github.com/GiovineItalia/Gadfly.jl) figures
- Supports various input format: Markdown, [Noweb](https://www.cs.tufts.edu/~nr/noweb/), [Jupyter Notebook](https://jupyter.org/), and ordinal Julia script
- Conversions between those input formats
- Supports various output document formats: HTML, PDF, GitHub markdown, Jupyter Notebook, MultiMarkdown, Asciidoc and reStructuredText
- Simple caching of results
* Noweb, markdown or script syntax for input documents.
* Execute code as terminal or "script" chunks.
* Capture Plots, Gadfly and PyPlot figures.
* Supports LaTex, Pandoc, Github markdown, MultiMarkdown, Asciidoc and reStructuredText output
* Publish markdown directly to html and pdf using julia or Pandoc markdown.
* Simple caching of results
* Convert to and from IJulia notebooks
![Weave in Juno demo](https://user-images.githubusercontent.com/40514306/76081328-32f41900-5fec-11ea-958a-375f77f642a2.png)
![Weave code and output](http://mpastell.com/images/weave_demo.png)
## Index
## Contents
```@contents
Pages = [
"index.md",
"getting_started.md",
"usage.md",
"publish.md",
"chunk_options.md",
"header.md",
"notebooks.md",
"function_index.md",
]
Pages = ["getting_started.md", "usage.md",
"publish.md", "chunk_options.md", "notebooks.md",
"function_index.md"]
```

View File

@ -1,36 +1,22 @@
# Working with Jupyter notebooks
## Weaving from Jupyter notebooks
## Weaving
Weave supports using [Jupyter Notebook](https://jupyter.org/)s as input format.
This means you can [`weave`](@ref) notebooks to any supported formats;
by default, it will be weaved to HTML.
Weave supports using Jupyter notebooks as input format, this means you
can weave notebooks to any supported formats. You can't use chunk options with notebooks.
```julia
weave("notebook.ipynb") # will be weaved to HTML
weave("notebook.ipynb")
```
!!! warning
You can't use chunk options with notebooks.
## Output to Jupyter notebooks
As of Weave 0.5.1. there is new [`notebook`](@ref) method to convert Weave documents to Jupyter notebooks using
[nbconvert](http://nbconvert.readthedocs.io/en/latest/execute_api.html).
```@docs
notebook
```
You can specify `jupyter` used to execute the notebook with the `jupyter_path` keyword argument
(this defaults to the `"jupyter"`, i.e. whatever you have linked to that location).
Instead, you might want to use the [`convert_doc`](@ref) method below and run the code in Jupyter.
In order to output notebooks from other formats you need to convert the
document to a notebook and run the code using IJulia.
## Converting between formats
You can convert between all supported input formats using the [`convert_doc`](@ref) function.
You can convert between all supported input formats using the `convert_doc`
function.
To convert from script to notebook:
@ -38,12 +24,12 @@ To convert from script to notebook:
convert_doc("examples/FIR_design.jl", "FIR_design.ipynb")
```
and from notebook to Markdown use:
and from notebooks to markdown use:
```julia
convert_doc("FIR_design.ipynb", "FIR_design.jmd")
```
```@docs
convert_doc
convert_doc(infile::String, outfile::String)
```

View File

@ -1,12 +1,10 @@
# Publishing to HTML and PDF
# Publishing to html and pdf
You can also publish any supported input format to HTML and PDF documents.
You can also publish any supported input format using markdown for doc chunks to html and pdf documents. Producing pdf output requires that you have pdflatex installed and in your path. *The markdown variant is [Julia markdown](http://docs.julialang.org/en/latest/manual/documentation.html#Markdown-syntax-1)*.
!!! note
Producing PDF output requires that you have XeLaTex installed and in your path.
You can use a YAML header in the beginning of the input document delimited with "---"
to set the document title, author and date e.g.
You can use a YAML header in the beginning of the input document delimited with `---`
to set the document title, author and date, e.g.:
```
---
title : Weave example
@ -15,53 +13,24 @@ date: 15th December 2016
---
```
Here are sample input and outputs:
- input (Julia markdown format): [`FIR_design_plots.jl`](../examples/FIR_design_plots.jl) (its path is bound to `Weave.SAMPLE_JL_DOC`)
- HTML output: [`FIR_design_plots.html`](../examples/FIR_design_plots.html)
- PDF output: [`FIR_design_plots.pdf`](../examples/FIR_design_plots.pdf)
Here is a a sample document and output:
[FIR_design.jl](examples/FIR_design.jl), [FIR_design.html](examples/FIR_design.html) , [FIR_design.pdf](examples/FIR_design.pdf).
They are generated as follows:
```julia
weave(Weave.SAMPLE_JL_DOC)) # default to md2html output format
weave(Weave.SAMPLE_JL_DOC; doctype = "md2pdf")
weave("FIR_design.jl")
weave("FIR_design.jl", docformat = "md2pdf")
```
!!! tips
`Weave.SAMPLE_JL_DOC` is the path of [FIR_design.jl](../examples/FIR_design.jl).
!!! note
`"md2html"` and `"md2pdf"` assume Julia markdown format as an input,
while `pandoc2pdf` and `pandoc2html` assume Noweb input format (i.e. Pandoc markdown).
**Note:** docformats `md2pdf` and `md2html` use Julia markdown and `pandoc2pdf` and `pandoc2html`
use Pandoc.
## Templates
You can use a custom template with `md2html` and `md2pdf` formats with `template` keyword option,
e.g.: `weave("FIR_design_plots.jl", template = "custom.tpl"`.
You can use a custom template with `md2pdf` and `md2html` formats with `template`
argument (e.g) `weave("FIR_design.jl", template = "custom.tpl"`). You can use
the existing templates as starting point.
As starting point, you can use the existing templates:
- HTML (`md2html`): [`md2html.tpl`](https://github.com/JunoLab/Weave.jl/blob/master/templates/md2html.tpl)
- LaTex (`md2pdf`): [`md2pdf.tpl`](https://github.com/JunoLab/Weave.jl/blob/master/templates/md2pdf.tpl)
For HTML: [julia_html.tpl](https://github.com/mpastell/Weave.jl/blob/master/templates/julia_html.tpl) and LaTex: [julia_tex.tpl](https://github.com/mpastell/Weave.jl/blob/master/templates/julia_tex.tpl)
Templates are rendered using [Mustache.jl](https://github.com/jverzani/Mustache.jl).
## Supported Markdown syntax
The markdown variant used by Weave is [Julia markdown](https://docs.julialang.org/en/v1/stdlib/Markdown/#).
In addition Weave supports few additional Markdown features:
### Comments
You can add comments using html syntax: `<!-- -->`
### Multiline equations
You can add multiline equations using:
```
$$
x^2 = x*x
$$
```

View File

@ -1,67 +1,77 @@
# Using Weave
You can write your documentation and code in input document using Markdown, Noweb or script
syntax and use [`weave`](@ref) function to execute to document to capture results and figures.
You can write your documentation and code in input document using Noweb, Markdown or script
syntax and use `weave` function to execute to document to capture results and figures.
## `weave`
## Weave
Weave document with markup and julia code using `Plots.jl` for plots,
Weave document with markup and julia code using Gadfly for plots,
`out_path = :pwd` makes the results appear in the current working directory.
> A prepared example: [`Weave.SAMPLE_JL_DOC`](../examples/FIR_design.jmd)
```julia
using Weave
weave(Pkg.dir("Weave","examples","gadfly_sample.mdw"), out_path = :pwd)
```
Using PyPlot:
```julia
# First add depencies for the example
using Pkg; Pkg.add.(["Plots", "DSP"])
using Weave
weave(Weave.SAMPLE_JL_DOC; out_path=:pwd)
weave(Pkg.dir("Weave","examples","julia_sample.mdw"), plotlib="PyPlot", out_path = :pwd)
```
```@docs
weave
weave(source)
```
## `tangle`
## Weave from shell
You can also use the `weave.jl` script under bin directory to weave documents
from the shell:
```
$ ./weave.jl
usage: weave.jl [--doctype DOCTYPE] [--plotlib PLOTLIB]
[--informat INFORMAT] [--out_path OUT_PATH]
[--fig_path FIG_PATH] [--fig_ext FIG_EXT] source...
```
## Tangle
Tangling extracts the code from document:
```@docs
tangle
tangle(source)
```
## Supported Output Formats
## Supported formats
Weave automatically detects the output format based on the file extension.
The auto output format detection is handled by `detect_doctype(path::AbstractString)`:
Weave sets the output format based on the file extension, but you can also set
it using `doctype` option. The rules for detecting the format are:
```julia
function detect_doctype(path::AbstractString)
_, ext = lowercase.(splitext(path))
match(r"^\.(jl|.?md|ipynb)", ext) !== nothing && return "md2html"
ext == ".rst" && return "rst"
ext == ".tex" && return "texminted"
ext == ".txt" && return "asciidoc"
return "pandoc"
end
ext == ".jl" && return "md2html"
contains(ext, ".md") && return "md2html"
contains(ext, ".rst") && return "rst"
contains(ext, ".tex") && return "texminted"
contains(ext, ".txt") && return "asciidoc"
return "pandoc"
```
You can also manually specify it using the `doctype` keyword option.
You can get a list of supported output formats:
```@docs
list_out_formats
```
```@example
using Weave # hide
list_out_formats()
```
## [Document Syntax](@id document-syntax)
```@docs
list_out_formats()
```
Weave uses markdown, Noweb or script syntax for defining the code chunks and
## Document syntax
Weave uses noweb, markdown or script syntax for defining the code chunks and
documentation chunks. You can also weave Jupyter notebooks. The format is detected based on the file extension, but you can also set it manually using the `informat` parameter.
The rules for autodetection are:
@ -73,110 +83,82 @@ ext == ".ipynb" && return "notebook"
return "noweb"
```
## Noweb format
### Documentation Chunks
### Code chunks
start with a line marked with `<<>>=` or `<<options>>=` and end with line marked with `@`. The code between the start and end markers is executed and the output is captured to the output document. See [chunk options](../chunk_options/).
In markdown and Noweb input formats documentation chunks are the parts that aren't inside code delimiters. Documentation chunks can be written with several different markup languages.
### Documentation chunks
Are the rest of the document (between `@` and `<<>>=` lines and the first chunk be default) and can be written with several different markup languages.
[Sample document]( https://github.com/mpastell/Weave.jl/blob/master/examples/julia_sample.mdw)
## Markdown format
Markdown code chunks are defined using fenced code blocks with options following on the same line. e.g. to hide code from output you can use:
` ```julia; echo=false`
[See sample document:](https://github.com/mpastell/Weave.jl/blob/master/examples/gadfly_md_sample.jmd)
## Script format
Weave also support script input format with a markup in comments.
These scripts can be executed normally using Julia or published with
Weave. Documentation is in lines starting with
`#'`, `#%%` or `# %%`, and code is executed and results are included
in the weaved document.
All lines that are not documentation are treated as code. You can set chunk options
using lines starting with `#+` just before code e.g. `#+ term=true`.
The format is identical to [Pweave](http://mpastell.com/pweave/pypublish.html)
and the concept is similar to publishing documents with MATLAB or
using Knitr's [spin](http://yihui.name/knitr/demo/stitch/).
Weave will remove the first empty space from each line of documentation.
### [Code Chunks](@id code-chunks)
[See sample document:](https://github.com/mpastell/Weave.jl/blob/master/examples/FIR_design.jl)
Code chunks are written in different ways in different formats.
#### Markdown Format
Weave code chunks are defined using fenced code blocks, same as with [common markdown](https://spec.commonmark.org/0.29/#fenced-code-blocks):
```markdown
```julia
code
...
```
```
Weave code chunks can optionally be followed by [chunk options](@ref) on the same line.
E.g. the chunk below will hide code itself from generated output:
```markdown
```julia, echo = false
code
...
```
```
#### Noweb Format
Code chunks start with a line marked with `<<>>=` or `<<options>>=` and end with line marked with `@`.
The code between the start and end markers is executed and the output is captured to the output document.
### [Inline Code](@id inline-code)
## Inline code
You can also add inline code to your documents using
```
`j juliacode`
```
or
```
! juliacode
```
syntax.
```
The former syntax allows you to insert code _anywhere_ in a line
while the `!` syntax treats the whole line as code,
and the code will be replaced with captured output in the weaved document.
syntax. The code will be replaced with the output of running the code.
If the code produces figures the filename or base64 encoded string will be
added to output e.g. to include a Plots figure in markdown you can use:
If the code produces figures, the filename or base64 encoded string will be added to output,
e.g. to include a Plots figure in markdown you can use:
```
![A plot](`j plot(1:10)`)
```
or to produce any HTML output:
```
! display("text/html", HTML("Header from julia"));
```
### Script Format
## Passing arguments to documents
Weave also supports script input format with a markup in comments.
These scripts can be executed normally using Julia or published with Weave.
You can pass arguments as dictionary to the weaved document using the `args` argument
to `weave`. The dictionary will be available as `WEAVE_ARGS` variable in the document.
Lines starting with `#'`, `#%%` or `# %%` are treated as 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.
In order to pass a filename to a document you need call `weave` using:
All non-document lines are treated as code.
You can set chunk options using lines starting with `#+` just before code e.g:
```julia
#+ term=true
hoge # some code comes here
weave("mydoc.jmd", args = Dict("filename" => "somedata.h5"))
```
The format is identical to [Pweave](http://mpastell.com/pweave/pypublish.html) and the concept is similar to publishing documents with MATLAB or using Knitr's [spin](http://yihui.name/knitr/demo/stitch/).
Weave will remove the first empty space from each line of documentation.
and you can access the filename from document as follows:
!!! tip
- Here are sample documents:
+ [markdown format](https://github.com/JunoLab/Weave.jl/blob/master/examples/FIR_design.jmd)
+ [script format](https://github.com/JunoLab/Weave.jl/blob/master/examples/FIR_design.jl)
- [Details about chunk options](@ref chunk-options)
## Configuration via YAML Header
When `weave`ing markdown files, you can use YAML header to provide additional metadata and configuration options.
See [Header Configuration](@ref) section for more details.
## Passing Runtime Arguments to Documents
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.
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`
You can call `include_weave` on a Weave document and run all code chunks within in the current session.
```@docs
include_weave
```
```julia
print(WEAVE_ARGS["filename"])
```
```
You can use the `out_path` argument to control the name of the
output document.

View File

@ -7,7 +7,7 @@
#' # Introduction
#' This an example of a julia script that can be published using
#' [Weave](http://weavejl.mpastell.com/dev/usage/).
#' [Weave](http://mpastell.github.io/Weave.jl/latest/usage/).
#' The script can be executed normally using Julia
#' or published to HTML or pdf with Weave.
#' Text is written in markdown in lines starting with "`#'` " and code
@ -34,9 +34,9 @@
using Gadfly, DSP
function FIRfreqz(b::Array, w = range(0, stop=π, length=1024))
function FIRfreqz(b::Array, w = linspace(0, π, 1024))
n = length(w)
h = Array{ComplexF32}(n)
h = Array{Complex64}(n)
sw = 0
for i = 1:n
for j = 1:length(b)
@ -60,7 +60,7 @@ end
fs = 20
f = digitalfilter(Lowpass(5, fs = fs), FIRWindow(hamming(61)))
w = range(0, stop=pi, length=1024)
w = linspace(0, pi, 1024)
h = FIRfreqz(f, w)
#' ## Plot the frequency and impulse response
@ -68,7 +68,7 @@ h = FIRfreqz(f, w)
#' The next code chunk is executed in term mode, see the [script](FIR_design.jl) for syntax.
#+ term=true
h_db = log10(abs.(h));
h_db = log10(abs(h));
ws = w/pi*(fs/2)
#+
@ -78,6 +78,6 @@ plot(y = h_db, x = ws, Geom.line,
#' And again with default options
h_phase = unwrap(-atan(imag(h),real(h)))
h_phase = unwrap(-atan2(imag(h),real(h)))
plot(y = h_phase, x = ws, Geom.line,
Guide.xlabel("Frequency (Hz)"), Guide.ylabel("Phase (radians)"))

View File

@ -1,13 +1,11 @@
#' ---
#' title: FIR filter design with Julia
#' author: Matti Pastell
#' date: 21th April 2016
#' ---
#' % FIR filter design with Julia
#' % Matti Pastell
#' % 21th April 2016
#' # Introduction
#' This an example of a julia script that can be published using
#' [Weave](http://weavejl.mpastell.com/dev/usage/).
#' [Weave](http://mpastell.github.io/Weave.jl/latest/usage/).
#' The script can be executed normally using Julia
#' or published to HTML or pdf with Weave.
#' Text is written in markdown in lines starting with "`#'` " and code
@ -33,11 +31,11 @@
#' the frequency response of a FIR filter so we define it:
using Plots, DSP
gr()
plotly()
function FIRfreqz(b::Array, w = range(0, stop=π, length=1024))
function FIRfreqz(b::Array, w = linspace(0, π, 1024))
n = length(w)
h = Array{ComplexF32}(undef, n)
h = Array{Complex64}(n)
sw = 0
for i = 1:n
for j = 1:length(b)
@ -62,7 +60,7 @@ end
fs = 20
f = digitalfilter(Lowpass(5, fs = fs), FIRWindow(hamming(61)))
w = range(0, stop=pi, length=1024)
w = linspace(0, pi, 1024)
h = FIRfreqz(f, w)
#' ## Plot the frequency and impulse response
@ -70,7 +68,7 @@ h = FIRfreqz(f, w)
#' The next code chunk is executed in term mode, see the [script](FIR_design.jl) for syntax.
#+ term=true
h_db = log10.(abs.(h));
h_db = log10(abs(h));
ws = w/pi*(fs/2)
#+
@ -80,6 +78,6 @@ plot(ws, h_db,
#' And again with default options
h_phase = unwrap(-atan.(imag.(h),real.(h)))
h_phase = unwrap(-atan2(imag(h),real(h)))
plot(ws, h_phase,
xlabel = "Frequency (Hz)", ylabel = "Phase (radians)")

View File

@ -1,46 +0,0 @@
\documentclass{beamer}
\usepackage{lmodern}
\usepackage{amssymb,amsmath}
\usepackage{bm}
\usepackage{graphicx}
\usepackage{microtype}
\usepackage{hyperref}
\setlength{\parindent}{0pt}
\setlength{\parskip}{1.2ex}
\hypersetup
{ pdfauthor = { {{{:author}}} },
pdftitle={ {{{:title}}} },
colorlinks=TRUE,
linkcolor=black,
citecolor=blue,
urlcolor=blue
}
{{#:title}}
\title{ {{{ :title }}} }
{{/:title}}
{{#:author}}
\author{ {{{ :author }}} }
{{/:author}}
{{#:date}}
\date{ {{{ :date }}} }
{{/:date}}
{{ :highlight }}
\begin{document}
{{{#:title}}}
\begin{frame}
\titlepage
\end{frame}
{{{/:title}}}
{{{ :body }}}
\end{document}

View File

@ -7,7 +7,7 @@ date : 13th December 2016
# Intro
This a sample [Julia](http://julialang.org/) markdown document that can
be executed using [Weave.jl](https://github.com/JunoLab/Weave.jl).
be executed using [Weave.jl](https://github.com/mpastell/Weave.jl).
The code is delimited from docs using markdown fenced code blocks
markup which can be seen looking at the source document
@ -46,7 +46,7 @@ output will be captured.
using Gadfly
x = linspace(0, 2*pi)
println(x)
plot(x = x, y = sin.(x))
plot(x = x, y = sin(x))
```
You can also control the way the results are captured, plot size etc.
@ -71,6 +71,6 @@ plot(y = cumsum(randn(1000, 1)), Geom.line)
Read the documentation:
- stable: [http://mpastell.github.io/Weave.jl/stable/](http://mpastell.github.io/Weave.jl/stable/)
- latest: [http://weavejl.mpastell.com/dev/](http://weavejl.mpastell.com/dev/)
- latest: [http://mpastell.github.io/Weave.jl/latest/](http://mpastell.github.io/Weave.jl/latest/)
See other examples in the [GitHub repo](https://github.com/JunoLab/Weave.jl/tree/master/examples)
See other examples in the [Github repo](https://github.com/mpastell/Weave.jl/tree/master/examples)

View File

@ -29,10 +29,10 @@
\section{Intro}
This is a minimal example on using PGF format with Gadfly plots in
\href{https://github.com/JunoLab/Weave.jl}{Weave.jl} document.
\href{https://github.com/mpastell/Weave.jl}{Weave.jl} document.
The source is in github:
\url{https://github.com/JunoLab/Weave.jl/blob/master/examples/gadfly_pgf.texw}.
\url{https://github.com/mpastell/Weave.jl/blob/master/examples/gadfly_pgf.texw}.
You can run this example with first weaving it from Julia using:

View File

@ -5,7 +5,7 @@
# Introduction
This a sample [Julia](http://julialang.org/) noweb document that can
be executed using [Weave.jl](https://github.com/JunoLab/Weave.jl).
be executed using [Weave.jl](https://github.com/mpastell/Weave.jl).
The code is delimited from docs using `<<>>=` and `@` markup which can be seen
looking at the source document `gadfly_sample.mdw` in the examples directory
@ -65,6 +65,6 @@ plot(y = cumsum(randn(1000, 1)), Geom.line)
Read the documentation:
- stable: <http://mpastell.github.io/Weave.jl/stable/>
- latest: <http://weavejl.mpastell.com/dev/>
- latest: <http://mpastell.github.io/Weave.jl/latest/>
See other examples in: <https://github.com/JunoLab/Weave.jl/tree/master/examples>
See other examples in: <https://github.com/mpastell/Weave.jl/tree/master/examples>

View File

@ -3,7 +3,7 @@
This a sample [Julia](http://julialang.org/) noweb document that can
be executed using Weave. Output from code chunks and PyPlot
plots will be included in the weaved document. You also need to install Pweave from GitHub in order to use Weave.
plots will be included in the weaved document. You also need to install Pweave from Github in order to use Weave.
This documented can be turned into Pandoc markdown with captured
result from Julia prompt.

View File

@ -1,29 +0,0 @@
---
title: A minimal beamer example using Weave markdown
author: Matti Pastell
weave_options:
doctype : md2pdf
out_path : pdf
template : beamer.tpl
---
```julia; echo=false
struct Begin
text
title
end
struct End
text
end
Base.show(io::IO, m::MIME"text/latex", b::Begin) = write(io, "\\begin{$(b.text)}[fragile]\n\\frametitle{$(b.title)}\n")
Base.show(io::IO, m::MIME"text/latex", e::End) = write(io, "\\end{$(e.text)}")
```
! Begin("frame", "Random plot")
```julia; out_width="0.5\\textwidth"
using Plots
scatter(randn(1000), randn(1000))
```
! End("frame")

View File

@ -1,344 +1,182 @@
__precompile__(false)
module Weave
import Highlights
using Highlights, Mustache, Requires, Pkg, REPL, RelocatableFolders, Base64
"""
`list_out_formats()`
# directories
const PKG_DIR = normpath(@__DIR__, "..")
const TEMPLATE_DIR = @path joinpath(PKG_DIR, "templates")
const STYLESHEET_DIR = @path joinpath(PKG_DIR, "stylesheets")
# keeps paths of sample documents for easy try
const EXAMPLE_FOLDER = @path joinpath(PKG_DIR, "examples")
# constant names
const WEAVE_OPTION_NAME = "weave_options"
const WEAVE_OPTION_NAME_DEPRECATED = "options" # remove this when tagging v0.11
const WEAVE_OPTION_DEPRECATE_ID = "weave_option_duplicate_id"
const DEFAULT_FIG_PATH = "figures"
const WEAVE_VERSION = try
'v' * Pkg.TOML.parsefile(normpath(PKG_DIR, "Project.toml"))["version"]
catch
""
end
weave_info() = WEAVE_VERSION, string(Date(now()))
function __init__()
@require Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" include("plots.jl")
@require Gadfly = "c91e804a-d5a3-530f-b6f0-dfbca275c004" include("gadfly.jl")
List supported output formats
"""
function list_out_formats()
for format = keys(formats)
println(string(format,": ", formats[format].description))
end
end
# utilitity functions
take2string!(io) = String(take!(io))
joinlines(lines) = join(lines, '\n')
include("types.jl")
include("config.jl")
include("WeaveMarkdown/markdown.jl")
include("display_methods.jl")
include("reader/reader.jl")
include("run.jl")
include("cache.jl")
include("rendering/rendering.jl")
include("writer/writer.jl")
include("converter.jl")
get_format(doctype::AbstractString) = FORMATS[doctype]
"""
list_out_formats()
List supported output formats with its description.
"""
list_out_formats() = [k => v.description for (k,v) in FORMATS]
"""
tangle(source::AbstractString; kwargs...)
`tangle(source ; out_path=:doc, informat="noweb")`
Tangle source code from input document to .jl file.
## Keyword options
- `informat::Union{Nothing,AbstractString} = nothing`: Input document format. By default (i.e. given `nothing`), Weave will set it automatically based on file extension. You can also specify either of `"script"`, `"markdown"`, `"notebook"`, or `"noweb"`
- `out_path::Union{Symbol,AbstractString} = :doc`: Path where the output is generated can be either of:
* `: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"`
* `informat`: `"noweb"` of `"markdown"`
* `out_path`: Path where the output is generated. Can be: `:doc`: Path of the source document, `:pwd`: Julia working directory, `"somepath"`, directory name as a string e.g `"/home/mpastell/weaveout"`
or filename as string e.g. ~/outpath/outfile.jl.
"""
function tangle(
source::AbstractString;
out_path::Union{Symbol,AbstractString} = :doc,
informat::Union{Nothing,AbstractString} = nothing,
)
doc = WeaveDoc(source, informat)
function tangle(source ; out_path=:doc, informat=:auto)
doc = read_doc(source, informat)
doc.cwd = get_cwd(doc, out_path)
out_path = get_out_path(doc, out_path, "jl")
outname = get_outname(out_path, doc, ext = "jl")
open(out_path, "w") do io
for chunk in doc.chunks
if typeof(chunk) == CodeChunk
options = merge(doc.chunk_defaults, chunk.options)
options[:tangle] && write(io, chunk.content * "\n")
end
end
open(outname, "w") do io
for chunk in doc.chunks
if typeof(chunk) == CodeChunk
options = merge(rcParams[:chunk_defaults], chunk.options)
if options[:tangle]
write(io, chunk.content*"\n")
end
end
end
@info "Tangled to $(out_path)"
end
doc.cwd == pwd() && (outname = basename(outname))
info("Writing to file $outname")
end
"""
weave(source::AbstractString; kwargs...)
weave(source ; doctype = :auto, plotlib=:auto,
informat=:auto, out_path=:doc, args = Dict(),
fig_path = "figures", fig_ext = nothing,
cache_path = "cache", cache=:off,
template = nothing, highlight_theme = nothing, css = nothing
latex_cmd = "pdflatex")
Weave an input document to output file.
## Keyword options
* `doctype`: :auto = set based on file extension or specify one of the supported formats.
See `list_out_formats()`
* `plotlib`: `"PyPlot"`, `"Gadfly"` or `nothing`
* `informat`: :auto = set based on file extension or set to `"noweb"`, `"markdown"` or `script`
* `out_path`: Path where the output is generated. Can be: `:doc`: Path of the source document, `:pwd`:
Julia working directory, `"somepath"`: output directory as a String e.g `"/home/mpastell/weaveout"` or filename as
string e.g. ~/outpath/outfile.tex.
* `args`: dictionary of arguments to pass to document. Available as WEAVE_ARGS
* `fig_path`: where figures will be generated, relative to out_path
* `fig_ext`: Extension for saved figures e.g. `".pdf"`, `".png"`. Default setting depends on `doctype`.
* `cache_path`: where of cached output will be saved.
* `cache`: controls caching of code: `:off` = no caching, `:all` = cache everything,
`:user` = cache based on chunk options, `:refresh`, run all code chunks and save new cache.
* `template` : Template (file path) for md2html or md2tex formats.
* `highlight_theme` : Theme (Highlights.AbstractTheme) for used syntax highlighting
* `css` : CSS (file path) used for md2html format
* `latex_cmd` the command used to make pdf from .tex
- `doctype::Union{Nothing,AbstractString} = nothing`: Output document format. By default (i.e. given `nothing`), Weave will set it automatically based on file extension. You can also manually specify it; see [`list_out_formats()`](@ref) for the supported formats
- `informat::Union{Nothing,AbstractString} = nothing`: Input document format. By default (i.e. given `nothing`), Weave will set it automatically based on file extension. You can also specify either of `"script"`, `"markdown"`, `"notebook"`, or `"noweb"`
- `out_path::Union{Symbol,AbstractString} = :doc`: Path where the output is generated can be either of:
* `: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::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`
- `cache_path::AbstractString = "cache"`: Where of cached output will be saved
- `cache::Symbol = :off`: Controls caching of code:
* `:off` means no caching (default)
* `:all` caches everything
* `:user` caches based on chunk options
* `:refresh` runs all code chunks and save new cache
- `template::Union{Nothing,AbstractString,Mustache.MustacheTokens} = nothing`: Template (file path) or `Mustache.MustacheTokens`s for `md2html` or `md2tex` formats
- `css::Union{Nothing,AbstractString} = nothing`: Path of a CSS file used for md2html format
- `highlight_theme::Union{Nothing,Type{<:Highlights.AbstractTheme}} = nothing`: Theme used for syntax highlighting (defaults to `Highlights.Themes.DefaultTheme`)
- `pandoc_options::Vector{<:AbstractString} = $(DEFAULT_PANDOC_OPTIONS)`: `String`s of options to pass to pandoc for `pandoc2html` and `pandoc2pdf` formats, e.g. `["--toc", "-N"]`
- `latex_cmd::Vector{<:AbstractString} = $(DEFAULT_LATEX_CMD)`: The command used to make PDF file from .tex
- `keep_unicode::Bool = false`: If `true`, do not convert unicode characters to their respective latex representation. This is especially useful if a font and tex-engine with support for unicode characters are used
!!! note
Run Weave from terminal and try to avoid weaving from IJulia or ESS; they tend to mess with capturing output.
**Note:** Run Weave from terminal and not using IJulia, Juno or ESS, they tend to mess with capturing output.
"""
function weave(
source::AbstractString;
doctype::Union{Nothing,AbstractString} = nothing,
informat::Union{Nothing,AbstractString} = nothing,
out_path::Union{Symbol,AbstractString} = :doc,
args::Any = Dict(),
mod::Union{Module,Nothing} = nothing,
fig_path::Union{Nothing,AbstractString} = nothing,
fig_ext::Union{Nothing,AbstractString} = nothing,
cache_path::AbstractString = "cache",
cache::Symbol = :off,
template::Union{Nothing,AbstractString,Mustache.MustacheTokens} = nothing,
css::Union{Nothing,AbstractString} = nothing, # TODO: rename to `stylesheet`
highlight_theme::Union{Nothing,Type{<:Highlights.AbstractTheme}} = nothing,
pandoc_options::Vector{<:AbstractString} = DEFAULT_PANDOC_OPTIONS,
latex_cmd::Vector{<:AbstractString} = DEFAULT_LATEX_CMD,
keep_unicode::Bool = false,
)
doc = WeaveDoc(source, informat)
function weave(source ; doctype = :auto, plotlib=:auto,
informat=:auto, out_path=:doc, args = Dict(),
fig_path = "figures", fig_ext = nothing,
cache_path = "cache", cache=:off,
template = nothing, highlight_theme = nothing, css = nothing,
latex_cmd = "pdflatex")
# run document
# ------------
doc = read_doc(source, informat)
highlight_theme != nothing && (doc.highlight_theme = highlight_theme)
#theme != nothing && (doc.theme = theme) #Reserved for themes
css != nothing && (doc.css = css)
template != nothing && (doc.template = template)
# overwrites options with those specified in header, that are needed for running document
# NOTE: these YAML options can NOT be given dynamically
weave_options = get(doc.header, WEAVE_OPTION_NAME, nothing)
if haskey(doc.header, WEAVE_OPTION_NAME_DEPRECATED)
@warn "Weave: `options` key is deprecated. Use `weave_options` key instead." _id = WEAVE_OPTION_DEPRECATE_ID maxlog = 1
weave_options = get(doc.header, WEAVE_OPTION_NAME_DEPRECATED, nothing)
end
if !isnothing(weave_options)
doctype = get(weave_options, "doctype", doctype)
specific_options!(weave_options, doctype)
if haskey(weave_options, "out_path")
out_path = let
out_path = weave_options["out_path"]
if out_path == ":doc" || out_path == ":pwd"
Symbol(out_path)
else
normpath(dirname(source), out_path) # resolve relative to this document
end
end
end
mod = get(weave_options, "mod", mod)
mod isa AbstractString && (mod = Main.eval(Meta.parse(mod)))
fig_path = get(weave_options, "fig_path", fig_path)
fig_ext = get(weave_options, "fig_ext", fig_ext)
cache_path = get(weave_options, "cache_path", cache_path)
cache = Symbol(get(weave_options, "cache", cache))
end
doc = run_doc(
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,
)
# overwrites options with those specified in header, that are needed for rendering/writing document
# NOTE: these YAML options can be given dynamically
if !isnothing(weave_options)
if haskey(weave_options, "template")
template = weave_options["template"]
# resolve relative to this document
template isa AbstractString && (template = normpath(dirname(source), template))
end
if haskey(weave_options, "css")
css = weave_options["css"]
# resolve relative to this document
css isa AbstractString && (css = normpath(dirname(source), css))
end
highlight_theme = get(weave_options, "highlight_theme", highlight_theme)
keep_unicode = get(weave_options, "keep_unicode", keep_unicode)
latex_cmd = get(weave_options, "latex_cmd", latex_cmd)
pandoc_options = get(weave_options, "pandoc_options", pandoc_options)
end
set_format_options!(
doc;
# general
template = template,
highlight_theme = highlight_theme,
css = css,
# pandoc
pandoc_options = pandoc_options,
# latex
keep_unicode = keep_unicode,
latex_cmd = latex_cmd,
)
# render document
# ---------------
rendered = render_doc(doc)
# write documents
# ---------------
out_path = write_doc(doc, rendered, get_out_path(doc, out_path))
@info "Weaved to $(out_path)"
return out_path
end
weave(doc::AbstractString, doctype::Union{Symbol,AbstractString}; kwargs...) =
weave(doc; doctype = doctype, kwargs...)
function specific_options!(weave_options, doctype)
fmts = keys(FORMATS)
for (k,v) in weave_options
if k in fmts
k == doctype && merge!(weave_options, v)
delete!(weave_options, k)
end
end
end
get_out_path(doc, out_path, ext::Nothing = nothing) = get_out_path(doc, out_path, doc.format.extension)
function get_out_path(doc, out_path, ext)
if (out_path === :doc) || (out_path === :pwd)
abspath(get_cwd(doc, out_path), string(doc.basename, '.', ext))
elseif isempty(splitext(out_path)[2]) # directory given
abspath(get_cwd(doc, out_path), string(doc.basename, '.', ext))
else
# out_path is given, but if extension is explitly provided override this will override the extension
abspath(string(splitext(out_path)[1], '.', ext))
end
end
"""
notebook(source::AbstractString; kwargs...)
Convert Weave document `source` to Jupyter Notebook and execute the code
using [`nbconvert`](https://nbconvert.readthedocs.io/en/latest/).
**Ignores** all chunk options.
## Keyword options
- `out_path::Union{Symbol,AbstractString} = :pwd`: Path where the output is generated can be either of:
* `:doc`: Path of the source document
* `:pwd`: Julia working directory (default)
* `"somepath"`: `String` of output directory e.g. `"~/outdir"`, or of filename e.g. `"~/outdir/outfile.tex"`
- `timeout = -1`: nbconvert cell timeout in seconds. Defaults to `-1` (no timeout)
- `nbconvert_options::AbstractString = ""`: `String` of additional options to pass to nbconvert, such as `"--allow-errors"`
- `jupyter_path::AbstractString = "jupyter"`: Path/command for the Jupyter you want to use. Defaults to `"jupyter"`, which runs whatever is linked/alias to that
!!! warning
The code is _**not**_ executed by Weave, but by [`nbconvert`](https://nbconvert.readthedocs.io/en/latest/).
This means that the output doesn't necessarily always work properly; see [#116](https://github.com/JunoLab/Weave.jl/issues/116).
!!! note
In order to _just_ convert Weave document to Jupyter Notebook,
use [`convert_doc`](@ref) instead.
"""
function notebook(
source::AbstractString;
out_path::Union{Symbol,AbstractString} = :pwd,
timeout = -1,
nbconvert_options::AbstractString = "",
jupyter_path::AbstractString = "jupyter",
)
doc = WeaveDoc(source)
converted = convert_to_notebook(doc)
doc.cwd = get_cwd(doc, out_path)
out_path = get_out_path(doc, out_path, "ipynb")
write(out_path, converted)
@info "Running nbconvert ..."
return read(
`$jupyter_path nbconvert --ExecutePreprocessor.timeout=$timeout --to notebook --execute $(out_path) $nbconvert_options --output $(out_path)`,
String,
)
end
"""
include_weave(source::AbstractString, informat::Union{Nothing,AbstractString} = nothing)
include_weave(m::Module, source::AbstractString, informat::Union{Nothing,AbstractString} = nothing)
Include code from Weave document calling `include_string` on all code from doc.
Code is run in the path of the include document.
"""
function include_weave(
m::Module,
source::AbstractString,
informat::Union{Nothing,AbstractString} = nothing,
)
old_path = pwd()
doc = WeaveDoc(source, informat)
cd(dirname(doc.path))
try
code = join(
[x.content for x in filter(x -> isa(x, Weave.CodeChunk), doc.chunks)],
"\n",
)
include_string(m, code)
catch err
throw(err)
doc = run(doc, doctype = doctype, plotlib=plotlib,
out_path=out_path, args = args,
fig_path = fig_path, fig_ext = fig_ext, cache_path = cache_path, cache=cache)
formatted = format(doc)
outname = get_outname(out_path, doc)
open(outname, "w") do io
write(io, formatted)
end
#Special for that need external programs
if doc.doctype == "pandoc2html"
mdname = outname
outname = get_outname(out_path, doc, ext = "html")
pandoc2html(formatted, doc, outname)
rm(mdname)
elseif doc.doctype == "pandoc2pdf"
mdname = outname
outname = get_outname(out_path, doc, ext = "pdf")
pandoc2pdf(formatted, doc, outname)
rm(mdname)
elseif doc.doctype == "md2pdf"
success = run_latex(doc, outname, latex_cmd)
success || return
outname = get_outname(out_path, doc, ext = "pdf")
end
doc.cwd == pwd() && (outname = basename(outname))
info("Report weaved to $outname")
catch e
warn("Something went wrong during weaving")
println(e)
finally
cd(old_path)
doctype == :auto && (doctype = detect_doctype(doc.source))
if contains(doctype, "2pdf") && cache == :off
rm(doc.fig_path, force = true, recursive = true)
elseif contains(doctype, "2html")
rm(doc.fig_path, force = true, recursive = true)
end
end
return nothing
end
include_weave(source, informat = nothing) = include_weave(Main, source, informat)
export weave,
list_out_formats,
tangle,
convert_doc,
notebook,
set_chunk_defaults!,
get_chunk_defaults,
restore_chunk_defaults!,
include_weave
function weave(doc::AbstractString, doctype::AbstractString)
weave(doc, doctype=doctype)
end
"""
include_weave(doc, informat=:auto)
Include code from Weave document calling `include_string` on
all code from doc. Code is run in the path of the include document.
"""
function include_weave(source, informat=:auto)
old_path = pwd()
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")
include_string(code)
catch e
cd(old_path)
throw(e)
end
end
#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(pretexecute_hooks, f))
const postexecute_hooks = Function[]
push_postexecute_hook(f::Function) = push!(postexecute_hooks, f)
pop_postexecute_hook(f::Function) = splice!(postexecute_hooks, findfirst(postexecute_hooks, f))
include("config.jl")
include("chunks.jl")
include("display_methods.jl")
include("readers.jl")
include("run.jl")
include("cache.jl")
include("formatters.jl")
include("format.jl")
include("pandoc.jl")
include("writers.jl")
export weave, list_out_formats, tangle, convert_doc,
set_chunk_defaults, get_chunk_defaults, restore_chunk_defaults,
include_weave
end

View File

@ -1,248 +0,0 @@
# 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
tohtml(io::IO, m::MIME"text/html", x) = show(io, m, x)
tohtml(io::IO, m::MIME"text/plain", x) = htmlesc(io, sprint(show, m, x))
function tohtml(io::IO, m::MIME"image/png", img)
print(io, """<img src="data:image/png;base64,""")
print(io, stringmime(m, img))
print(io, "\" />")
end
tohtml(m::MIME"image/svg+xml", img) = show(io, m, img)
# AbstractDisplay infrastructure
function bestmime(val)
for mime in ("text/html", "image/svg+xml", "image/png", "text/plain")
showable(mime, val) && return MIME(Symbol(mime))
end
error("Cannot render $val to Markdown.")
end
tohtml(io::IO, x) = tohtml(io, bestmime(x), x)
# Utils
function withtag(f, io::IO, tag, attrs...)
print(io, "<$tag")
for (attr, value) in attrs
print(io, " ")
htmlesc(io, attr)
print(io, "=\"")
htmlesc(io, value)
print(io, "\"")
end
f === nothing && return print(io, " />")
print(io, ">")
f()
print(io, "</$tag>")
end
tag(io::IO, tag, attrs...) = withtag(nothing, io, tag, attrs...)
const _htmlescape_chars = Dict(
'<' => "&lt;",
'>' => "&gt;",
'"' => "&quot;",
'&' => "&amp;",
# ' '=>"&nbsp;",
)
for ch in "'`!\$%()=+{}[]"
_htmlescape_chars[ch] = "&#$(Int(ch));"
end
function htmlesc(io::IO, s::AbstractString)
# s1 = replace(s, r"&(?!(\w+|\#\d+);)" => "&amp;")
for ch in s
print(io, get(_htmlescape_chars, ch, ch))
end
end
function htmlesc(io::IO, s::Symbol)
htmlesc(io, string(s))
end
function htmlesc(io::IO, xs::Union{AbstractString,Symbol}...)
for s in xs
htmlesc(io, s)
end
end
function htmlesc(s::Union{AbstractString,Symbol})
sprint(htmlesc, s)
end
# Block elements
function html(io::IO, content::Vector)
for md in content
html(io, md)
println(io)
end
end
html(io::IO, md::MD) = html(io, md.content)
function html(io::IO, header::Header{l}) where {l}
withtag(io, "h$l") do
htmlinline(io, header.text)
end
end
function html(io::IO, code::Code)
withtag(io, :pre) do
maybe_lang = !isempty(code.language) ? Any[:class=>"language-$(code.language)"] : []
withtag(io, :code, maybe_lang...) do
htmlesc(io, code.code)
# TODO should print newline if this is longer than one line ?
end
end
end
function html(io::IO, md::Paragraph)
withtag(io, :p) do
htmlinline(io, md.content)
end
end
function html(io::IO, md::BlockQuote)
withtag(io, :blockquote) do
println(io)
html(io, md.content)
end
end
function html(io::IO, f::Footnote)
withtag(io, :div, :class => "footnote", :id => "footnote-$(f.id)") do
withtag(io, :p, :class => "footnote-title") do
print(io, f.id)
end
html(io, f.text)
end
end
function html(io::IO, md::Admonition)
withtag(io, :div, :class => "admonition $(md.category)") do
withtag(io, :p, :class => "admonition-title") do
print(io, md.title)
end
html(io, md.content)
end
end
function html(io::IO, md::List)
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)
withtag(io, :li) do
html(io, item)
end
end
println(io)
end
end
html(io::IO, md::HorizontalRule) = tag(io, :hr)
function html(io::IO, tex::LaTeX)
withtag(io, :p, :class => "math") do
write(io, string("\\[\n", tex.formula, "\n\\]"))
end
end
html(io::IO, comment::Comment) = write(io, "\n<!-- $(comment.text) -->\n")
function html(io::IO, md::Table)
withtag(io, :table) do
for (i, row) in enumerate(md.rows)
withtag(io, :tr) do
for c in md.rows[i]
withtag(io, i == 1 ? :th : :td) do
htmlinline(io, c)
end
end
end
end
end
end
html(io::IO, x) = tohtml(io, x)
# Inline elements
function htmlinline(io::IO, content::Vector)
for x in content
htmlinline(io, x)
end
end
function htmlinline(io::IO, code::Code)
withtag(io, :code) do
htmlesc(io, code.code)
end
end
function htmlinline(io::IO, tex::LaTeX)
withtag(io, :span, :class => "math") do
write(io, string("\$", tex.formula, "\$"))
end
end
htmlinline(io::IO, md::Union{Symbol,AbstractString}) = htmlesc(io, md)
function htmlinline(io::IO, md::Bold)
withtag(io, :strong) do
htmlinline(io, md.text)
end
end
function htmlinline(io::IO, md::Italic)
withtag(io, :em) do
htmlinline(io, md.text)
end
end
htmlinline(io::IO, md::Image) = tag(io, :img, :src => md.url, :alt => md.alt)
function htmlinline(io::IO, f::Footnote)
withtag(io, :a, :href => "#footnote-$(f.id)", :class => "footnote") do
print(io, "[", f.id, "]")
end
end
function htmlinline(io::IO, link::Link)
withtag(io, :a, :href => link.url) do
htmlinline(io, link.text)
end
end
htmlinline(io::IO, br::LineBreak) = tag(io, :br)
htmlinline(io::IO, comment::Comment) = write(io, "<!-- $(comment.text) -->")
htmlinline(io::IO, x) = tohtml(io, x)
# API
html(md) = sprint(html, md)

View File

@ -1,9 +0,0 @@
import Markdown: latex, latexinline
function latex(io::IO, comment::Comment)
for line in split(comment.text, r"\r\n|\n")
write(io, "% $line\n")
end
end
latexinline(io, comment::Comment) = write(io, "")

View File

@ -1,92 +0,0 @@
# This module extends the julia markdown parser to improve compatibility with Jupyter, Pandoc etc.
module WeaveMarkdown
using ..Weave: isnothing, take2string!
using Markdown
import Markdown: @trigger, @breaking, Code, MD, withstream, startswith, LaTeX
# HACK: that this definition is type-piracy. It is required since `Markdown`
# does not have a built in system for contextual rendering by users. `io` here
# should always be either `IOBuffer` or `IOContext` since it is reached via
# `sprint` in all user-facing code paths in `Markdown`.
function Markdown.latex(io::Union{IOBuffer,IOContext}, 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
text::String
end
@breaking true -> function dollarmath(stream::IO, block::MD)
withstream(stream) do
str = Markdown.startswith(stream, r"^\$\$$"m)
isempty(str) && return false
trailing = strip(readline(stream))
buffer = IOBuffer()
while !eof(stream)
line_start = position(stream)
estr = Markdown.startswith(stream, r"^\$\$$"m)
if !isempty(estr)
estr = Markdown.startswith(stream, r"^\$\$$"m)
if isempty(estr)
push!(block, LaTeX(take2string!(buffer) |> chomp))
end
return true
else
seek(stream, line_start)
end
write(buffer, readline(stream, keep = true))
end
return false
end
end
@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)
write(buffer, line)
if occursin(r"-->$", line)
s = replace(take2string!(buffer) |> chomp, r"-->$" => "")
push!(block, Comment(s))
return true
end
end
return false
end
end
@trigger '<' -> function comment(stream::IO, md::MD)
withstream(stream) do
Markdown.startswith(stream, "<!--") || return
text = Markdown.readuntil(stream, "-->")
isnothing(text) && return
return Comment(text)
end
end
# Create own flavor and copy all the features from julia flavor
Markdown.@flavor weavemd [dollarmath, comment, topcomment]
weavemd.breaking = [weavemd.breaking; Markdown.julia.breaking]
weavemd.regular = [weavemd.regular; Markdown.julia.regular]
for key in keys(Markdown.julia.inner)
if haskey(weavemd.inner, key)
weavemd.inner[key] = [weavemd.inner[key]; Markdown.julia.inner[key]]
else
weavemd.inner[key] = Markdown.julia.inner[key]
end
end
include("html.jl")
include("latex.jl")
end # module

View File

@ -1,57 +1,53 @@
# Serialization is imported only if cache is used
import JLD
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
Serialization.serialize(io, doc)
end
isdir(cache_dir) || mkpath(cache_dir)
JLD.save(joinpath(cache_dir, doc.basename * ".jld"), Dict("doc" => doc))
return nothing
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
doc = Serialization.deserialize(io)
end
return doc
name = joinpath(doc.cwd, cache_path, doc.basename * ".jld")
isfile(name) || return nothing
return JLD.load(name)["doc"]
end
function restore_chunk(chunk::CodeChunk, cached)
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
new_chunks = []
function restore_chunk(chunk::CodeChunk, cached)
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
new_chunks = Any[]
for c in chunks
newc = CodeChunk(c.content, c.number, c.start_line, c.optionstring, c.options)
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
n = length(chunk.content)
for i = 1:n
#Restore cached results for Inline code
n = length(chunk.content)
for i in 1:n
if isa(chunk.content[i], InlineCode)
ci = filter(x -> x.number == chunk.content[i].number, c_inline)
isempty(ci) && continue

89
src/chunks.jl Normal file
View File

@ -0,0 +1,89 @@
using Compat
@compat abstract type WeaveChunk end
@compat abstract type Inline end
type WeaveDoc
source::AbstractString
basename::AbstractString
path::AbstractString
chunks::Array{WeaveChunk}
cwd::AbstractString
format
doctype::AbstractString
header_script::String
header::Dict
template::AbstractString
css::AbstractString
highlight_theme
fig_path::AbstractString
function WeaveDoc(source, chunks, header)
path, fname = splitdir(abspath(source))
basename = splitext(fname)[1]
new(source, basename, path, chunks, "", nothing, "", "", header,
"", "", Highlights.Themes.DefaultTheme, "")
end
end
immutable ChunkOutput
code::AbstractString
stdout::AbstractString
displayed::AbstractString
rich_output::AbstractString
figures::Array{AbstractString}
end
type CodeChunk <: WeaveChunk
content::AbstractString
number::Int
result_no::Int
start_line::Int
optionstring::AbstractString
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[])
end
end
type DocChunk <: WeaveChunk
content::Array{Inline}
number::Int
start_line::Int
function DocChunk(text::AbstractString, number::Int, start_line::Int, inline_regex = nothing)
chunks = parse_inline(text, inline_regex)
new(chunks, number, start_line)
end
end
type InlineText <: Inline
content::AbstractString
si::Int
ei::Int
number::Int
end
type InlineCode <: Inline
content::AbstractString
si::Int
ei::Int
number::Int
output::AbstractString
rich_output::AbstractString
figures::Array{AbstractString}
function InlineCode(content, si, ei, number)
new(content, si, ei, number, "", "", AbstractString[])
end
end
type TermResult
end
type ScriptResult
end
type CollectResult
end

View File

@ -1,58 +1,88 @@
# TODO: follow RMarkdown convention more
const _DEFAULT_PARAMS = Dict{Symbol,Any}(
:echo => true,
:results => "markup",
:hold => false,
:fig => true,
:eval => true,
:error => true,
:tangle => true,
:cache => false,
:fig_cap => nothing,
# NOTE: size in inches
:fig_width => 6,
:fig_height => 4,
:fig_path => DEFAULT_FIG_PATH,
:dpi => 96,
:term => false,
:prompt => "julia>",
:label => nothing,
:wrap => true,
:line_width => 75,
:fig_ext => nothing,
:fig_pos => nothing,
:fig_env => nothing,
:out_width => nothing,
:out_height => nothing,
)
const DEFAULT_PARAMS = deepcopy(_DEFAULT_PARAMS) # might be changed at runtime
#Default options
const defaultParams =
Dict{Symbol,Any}(:plotlib => nothing,
:plotlib_set => false,
: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> ",
:name=> 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,
)
)
#This one can be changed at runtime, initially a copy of defaults
const rcParams = deepcopy(defaultParams)
#Parameters set per document
const docParams =Dict{Symbol,Any}(
:fig_path=> nothing,
:fig_ext => nothing,
)
"""
set_chunk_defaults!(k, v)
set_chunk_defaults!(kv::Pair...)
set_chunk_defaults!(opts::AbstractDict)
`set_chunk_defaults(opts::Dict{Symbol, Any})`
Set default options for code chunks, use [`get_chunk_defaults`](@ref) to see the current values.
Set default options for code chunks, use get_chunk_defaults
to see the current values.
E.g.: all the three examples below will set default `dpi` to `200` and `fig_width` to `8`:
- `set_chunk_defaults!(:dpi, 200); set_chunk_defaults!(:fig_width, 8)`
- `set_chunk_defaults!(:dpi => 200, :fig_width => 8)`
- `set_chunk_defaults!(Dict(:dpi => 200, :fig_width => 8))`
e.g. set default dpi to 200 and fig_width to 8
```
julia> set_chunk_defaults(Dict{Symbol, Any}(:dpi => 200, fig_width => 8))
```
"""
set_chunk_defaults!(k, v) = DEFAULT_PARAMS[k]= v
set_chunk_defaults!(kv::Pair...) = for (k,v) in kv; set_chunk_defaults!(k, v); end
set_chunk_defaults!(opts::AbstractDict) = merge!(DEFAULT_PARAMS, opts)
function set_chunk_defaults(opts::Dict{Symbol, Any})
merge!(rcParams[:chunk_defaults], opts)
return nothing
end
"""
get_chunk_defaults()
`get_chunk_defaults()`
Get default options used for code chunks.
"""
get_chunk_defaults() = DEFAULT_PARAMS
function get_chunk_defaults()
return(rcParams[:chunk_defaults])
end
"""
restore_chunk_defaults!()
`restore_chunk_defaults()`
Restore Weave.jl default chunk options.
Restore Weave.jl default chunk options
"""
restore_chunk_defaults!() = for (k,v) in _DEFAULT_PARAMS; DEFAULT_PARAMS[k] = v; end
function restore_chunk_defaults()
rcParams[:chunk_defaults] = defaultParams[:chunk_defaults]
merge!(rcParams[:chunk_defaults], docParams)
return nothing
end

View File

@ -1,144 +0,0 @@
using JSON, Mustache
"""
convert_doc(infile::AbstractString, outfile::AbstractString; outformat::Union{Nothing,AbstractString} = nothing)
Convert Weave documents between different formats
- `infile`: Path of the input document
- `outfile`: Path of the output document
- `outformat = nothing`: Output document format (optional). By default (i.e. given `nothing`) Weave will try to automatically detect it from the `outfile`'s extension. You can also specify either of `"script"`, `"markdown"`, `"notebook"`, or `"noweb"`
"""
function convert_doc(
infile::AbstractString,
outfile::AbstractString;
outformat::Union{Nothing,AbstractString} = nothing,
)
doc = WeaveDoc(infile)
if isnothing(outformat)
ext = lowercase(splitext(outfile)[2])
outformat =
ext == ".jl" ? "script" :
ext == ".jmd" ? "markdown" :
ext == ".ipynb" ? "notebook" :
"noweb" # fallback
end
converted = _convert_doc(doc, outformat)
open(outfile, "w") do f
write(f, converted)
end
return outfile
end
function _convert_doc(doc, outformat)
outformat == "script" ? convert_to_script(doc) :
outformat == "markdown" ? convert_to_markdown(doc) :
outformat == "notebook" ? convert_to_notebook(doc) :
convert_to_noweb(doc)
end
function convert_to_script(doc)
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
return output
end
function convert_to_markdown(doc)
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
return output
end
function convert_to_notebook(doc)
nb = Dict()
nb["nbformat"] = 4
nb["nbformat_minor"] = 2
metadata = Dict()
kernelspec = Dict()
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["version"] = "$(VERSION.major).$(VERSION.minor).$(VERSION.patch)"
metadata["language_info"] = language_info
cells = []
ex_count = 1
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], ""))],
),
)
else
push!(
cells,
Dict(
"cell_type" => "code",
"metadata" => Dict(),
"source" => [strip(chunk.content)],
"execution_count" => nothing,
"outputs" => [],
),
)
end
end
nb["cells"] = cells
nb["metadata"] = metadata
json_nb = JSON.json(nb, 2)
return json_nb
end
function convert_to_noweb(doc)
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
return output
end
Base.repr(c::InlineText) = c.content
Base.repr(c::InlineCode) = "`j $(c.content)`"

View File

@ -1,133 +1,102 @@
using Markdown, .WeaveMarkdown
# Contains report global properties
mutable struct Report <: AbstractDisplay
cwd::String
basename::String
format::WeaveFormat
rich_output::String
fignum::Int
figures::Vector{String}
cur_chunk::Union{Nothing,CodeChunk}
mimetypes::Vector{String}
first_plot::Bool
header_script::String
#Contains report global properties
type Report <: Display
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
end
Report(cwd, basename, format, mimetypes) =
Report(cwd, basename, format, "", 1, String[], nothing, mimetypes, true, "")
function Report(cwd, basename, formatdict, mimetypes)
Report(cwd, basename, formatdict, "", "", "", 1, AbstractString[], :text, nothing, mimetypes, true, "")
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 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 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" ]
function Base.display(report::Report, data)
# 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])
if Base.invokelatest(showable, m, data)
#Set preferred mimetypes for report based on format
for m in report.mimetypes
if mimewritable(m, data)
try
if !istextmime(m)
Base.invokelatest(display, report, m, data)
elseif report.cur_chunk.options[:term]
Base.invokelatest(display, report, "text/plain", data)
else
Base.invokelatest(display, report, m, data)
end
if VERSION >= v"0.6.0-dev.1671"
new_dp(x, y, z) = eval(
Expr(:call, (x, y, z) -> display(x, y, z), x, y, z))
new_dp(report, m, data)
else
display(report, m, data)
end
catch e
throw(e)
@warn("Failed to display data in \"$m\" format")
warn("Failed to display data in \"$m\" format")
continue
end
#Always show plain text as well for term mode
if m "text/plain" && report.cur_chunk.options[:term]
display(report, "text/plain", data)
end
break
end
end
end
Base.display(report::Report, m::MIME"image/png", data) = add_figure(report, data, m, ".png")
function Base.display(report::Report, m::MIME"image/png", data)
figname = add_figure(report, data, m, ".png")
end
Base.display(report::Report, m::MIME"image/svg+xml", data) = add_figure(report, data, m, ".svg")
function Base.display(report::Report, m::MIME"image/svg+xml", data)
figname = add_figure(report, data, m, ".svg")
end
Base.display(report::Report, m::MIME"application/pdf", data) = add_figure(report, data, m, ".pdf")
function Base.display(report::Report, m::MIME"application/pdf", data)
figname = add_figure(report, m, data, ".pdf")
end
# Text is written to stdout, called from "term" mode chunks
#Text is written to stdout, called from "term" mode chunks
function Base.display(report::Report, m::MIME"text/plain", data)
io = PipeBuffer()
show(IOContext(io, :limit => true), m, data)
flush(io)
s = read(io, String)
close(io)
s = reprmime(m, data)
println(s)
end
function Base.display(report::Report, m::MIME"text/plain", data::Exception)
println("Error: " * sprint(showerror, data))
end
function Base.display(report::Report, m::MIME"text/html", data::Exception)
report.rich_output = sprint(show, m, data)
end
function Base.show(io, m::MIME"text/html", data::Exception)
println(io, "<pre class=\"julia-error\">")
println(io, Markdown.htmlesc("ERROR: " * sprint(showerror, data)))
println(io, "</pre>")
end
# Catch "rich_output"
#Catch "rich_output"
function Base.display(report::Report, m::MIME"text/html", data)
io = IOBuffer()
show(IOContext(io, :limit => true), m, data)
report.rich_output *= string('\n', take2string!(io))
s = reprmime(m, data)
report.rich_output *= "\n" * s
end
# Catch "rich_output"
#Catch "rich_output"
function Base.display(report::Report, m::MIME"text/markdown", data)
s = repr(m, 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))
break
elseif m == "text/markdown"
report.rich_output *= "\n" * s
break
end
end
s = reprmime(m, data)
report.rich_output *= "\n" * s
end
function Base.display(report::Report, m::MIME"text/latex", data)
s = repr(m, data)
report.rich_output *= string('\n', s)
s = reprmime(m, data)
report.rich_output *= "\n" * s
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
show(io, m, data)
end
push!(report.figures, rel_name)
report.fignum += 1
return full_name
push!(report.figures, rel_name)
report.fignum += 1
return full_name
end

335
src/format.jl Normal file
View File

@ -0,0 +1,335 @@
import Mustache, Highlights, Documenter
function format(doc::WeaveDoc)
formatted = AbstractString[]
docformat = doc.format
#Complete format dictionaries with defaults
formatdict = docformat.formatdict
get!(formatdict, :termstart, formatdict[:codestart])
get!(formatdict, :termend, formatdict[:codeend])
get!(formatdict, :out_width, nothing)
get!(formatdict, :out_height, nothing)
get!(formatdict, :fig_pos, nothing)
get!(formatdict, :fig_env, nothing)
docformat.formatdict[:cwd] = doc.cwd #pass wd to figure formatters
docformat.formatdict[:theme] = doc.highlight_theme
#strip header
if isa(doc.chunks[1], DocChunk)
if contains(doc.doctype, "md2")
doc.chunks[1] = strip_header(doc.chunks[1])
end
end
for chunk in copy(doc.chunks)
result = format_chunk(chunk, formatdict, docformat)
push!(formatted, result)
end
formatted = join(formatted, "\n")
# Render using a template if needed
rendered = render_doc(formatted, doc, doc.format)
return rendered
end
"""
render_doc(formatted::AbstractString, format)
Render formatted document to a template
"""
function render_doc(formatted, doc::WeaveDoc, format)
return formatted
end
function stylesheet(m::MIME, theme)
buf = PipeBuffer()
Highlights.stylesheet(buf, m, theme)
flush(buf)
style = readstring(buf)
close(buf)
return style
end
function render_doc(formatted, doc::WeaveDoc, format::JMarkdown2HTML)
css = stylesheet(MIME("text/html"), doc.highlight_theme)
title, author, date = get_titleblock(doc)
path, wsource = splitdir(abspath(doc.source))
wversion = string(Pkg.installed("Weave"))
wtime = string(Date(now()))
if isempty(doc.css)
theme_css = readstring(joinpath(dirname(@__FILE__), "../templates/skeleton_css.css"))
else
theme_css = readstring(doc.css)
end
if 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,
title = title, author = author, date = date)
end
function render_doc(formatted, doc::WeaveDoc, format::JMarkdown2tex)
highlight = stylesheet(MIME("text/latex"), doc.highlight_theme)
title, author, date = get_titleblock(doc)
path, wsource = splitdir(abspath(doc.source))
wversion = string(Pkg.installed("Weave"))
wtime = string(Date(now()))
if 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,
title = title, author = author, date = date)
end
function get_titleblock(doc::WeaveDoc)
title = get!(doc.header, "title", false)
author = get!(doc.header, "author", false)
date = get!(doc.header, "date", false)
return title, author, date
end
function strip_header(chunk::DocChunk)
if ismatch(r"^---$(?<header>.+)^---$"ms, chunk.content[1].content)
chunk.content[1].content = lstrip(replace(chunk.content[1].content, r"^---$(?<header>.+)^---$"ms, ""))
end
return chunk
end
function format_chunk(chunk::DocChunk, formatdict, docformat)
return join([format_inline(c) for c in chunk.content], "")
end
function format_inline(inline::InlineText)
return inline.content
end
function format_inline(inline::InlineCode)
isempty(inline.rich_output) || return inline.rich_output
isempty(inline.figures) || return inline.figures[end]
isempty(inline.output) || return inline.output
end
function format_chunk(chunk::DocChunk, formatdict, docformat::JMarkdown2HTML)
text = format_chunk(chunk, formatdict, nothing)
m = Base.Markdown.parse(text)
return string(Documenter.Writers.HTMLWriter.mdconvert(m))
end
if VERSION < v"0.6.0-dev.1965"
#Fixes to Base latex writer, included in JuliaLang/julia#19842 and JuliaLang/julia#19832.
function Base.Markdown.latex(io::IO, md::Base.Markdown.Paragraph)
println(io)
for md in md.content
Base.Markdown.latexinline(io, md)
end
println(io)
end
function wrapverb(f, io, cmd)
print(io, "\\", cmd, "|")
f()
print(io, "|")
end
function Base.Markdown.latexinline(io::IO, code::Base.Markdown.Code)
wrapverb(io, "verb") do
print(io, code.code)
end
end
end
function format_chunk(chunk::DocChunk, formatdict, docformat::JMarkdown2tex)
text = format_chunk(chunk, formatdict, nothing)
m = Base.Markdown.parse(text)
return Base.Markdown.latex(m)
end
function format_chunk(chunk::CodeChunk, formatdict, docformat)
#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])
#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])
end
if haskey(formatdict, :indent)
chunk.content = indent(chunk.content, formatdict[:indent])
end
chunk.content = format_code(chunk.content, docformat)
if !chunk.options[:eval]
if chunk.options[:echo]
result = "$(formatdict[:codestart])\n$(chunk.content)$(formatdict[:codeend])"
return result
else
r = ""
return r
end
end
if chunk.options[:term]
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")
chunk.output = format_output(chunk.output, docformat)
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])
else
chunk.output = "\n" * rstrip(chunk.output)
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
#Handle figures
if chunk.options[:fig] && length(chunk.figures) > 0
if chunk.options[:include]
result *= formatfigures(chunk, docformat)
end
end
return result
end
function format_output(result::AbstractString, docformat)
return(result)
end
function format_output(result::AbstractString, docformat::JMarkdown2HTML)
return(Base.Markdown.htmlesc(result))
end
function format_code(result::AbstractString, docformat)
return result
end
function format_code(result::AbstractString, docformat::JMarkdown2tex)
buf = PipeBuffer()
Highlights.highlight(buf, MIME("text/latex"), strip(result),
Highlights.Lexers.JuliaLexer, docformat.formatdict[:theme])
flush(buf)
highlighted = readstring(buf)
close(buf)
return highlighted
end
function format_code(result::AbstractString, docformat::JMarkdown2HTML)
buf = PipeBuffer()
Highlights.highlight(buf, MIME("text/html"), strip(result),
Highlights.Lexers.JuliaLexer, docformat.formatdict[:theme])
flush(buf)
highlighted = readstring(buf)
close(buf)
return highlighted
end
function format_termchunk(chunk, formatdict, docformat)
if chunk.options[:echo] && chunk.options[:results] != "hidden"
result = "$(formatdict[:termstart])$(chunk.output)\n" * "$(formatdict[:termend])\n"
else
result = ""
end
return result
end
function format_termchunk(chunk, formatdict, docformat::JMarkdown2HTML)
if chunk.options[:echo] && chunk.options[:results] != "hidden"
buf = PipeBuffer()
Highlights.highlight(buf, MIME("text/html"), strip(chunk.output), Highlights.Lexers.JuliaConsoleLexer)
flush(buf)
result = readstring(buf)
close(buf)
else
result = ""
end
return result
end
function format_termchunk(chunk, formatdict, docformat::JMarkdown2tex)
if chunk.options[:echo] && chunk.options[:results] != "hidden"
buf = PipeBuffer()
Highlights.highlight(buf, MIME("text/latex"), strip(chunk.output), Highlights.Lexers.JuliaConsoleLexer)
flush(buf)
result = readstring(buf)
close(buf)
else
result = ""
end
return result
end
function indent(text, nindent)
return join(map(x->
string(repeat(" ", nindent), x), split(text, "\n")), "\n")
end
function wraplines(text, line_width=75)
result = AbstractString[]
lines = split(text, "\n")
for line in lines
if length(line) > line_width
push!(result, wrapline(line, line_width))
else
push!(result, line)
end
end
#return result
return strip(join(result, "\n"))
end
function wrapline(text, line_width=75)
result = ""
while length(text) > line_width
result*= text[1:line_width] * "\n"
text = text[(line_width+1):end]
end
result *= text
end

527
src/formatters.jl Normal file
View File

@ -0,0 +1,527 @@
type Tex
description::AbstractString
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"]
))
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]{julia}",
: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"]
))
type Pandoc
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 pdoc2html = Pandoc("Markdown to HTML (requires Pandoc)",
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/html", "text/markdown", "text/plain"],
:doctype=> "pandoc2html"))
type Markdown
description::AbstractString
formatdict::Dict{Symbol,Any}
end
const github = Markdown("Github markdown",
Dict{Symbol,Any}(
:codestart => "````julia",
:codeend=> "````\n\n",
:outputstart=> "````",
:outputend=> "````\n\n",
:fig_ext=> ".png",
:extension=> "md",
:doctype=> "github"
))
"""
Formatter for Hugo: https://gohugo.io/
When `uglyURLs` is `false`, prepend figure path by `..`.
"""
immutable Hugo
description::AbstractString
formatdict::Dict{Symbol,Any}
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)
#Julia markdown
type JMarkdown2HTML
description::AbstractString
formatdict::Dict{Symbol,Any}
end
const md2html = JMarkdown2HTML("Julia markdown to html", Dict{Symbol,Any}(
:codestart => "\n",
:codeend=> "\n",
:outputstart=> "<pre class=\"hljl\">",
: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
type 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",
:mimetypes => ["application/pdf", "image/png", "image/jpg",
"text/latex", "text/plain"],
:doctype=> "md2tex"))
type MultiMarkdown
description::AbstractString
formatdict::Dict{Symbol,Any}
end
function formatfigures(chunk, docformat::JMarkdown2HTML)
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 = ""
#Set size
attribs = ""
width == nothing || (attribs = "width=\"$width\"")
(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"""
end
result *= figstring
if caption != nothing
result *= """
<figcaption>$caption</figcaption>
"""
end
if caption != nothing
result *= "</figure>\n"
end
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"
))
type 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"
))
type 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 formatfigures(chunk, docformat::Tex)
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]
if f_env == nothing && caption != nothing
f_env = "figure"
end
f_pos == nothing && (f_pos = "!h")
result = ""
figstring = ""
#Set size
attribs = ""
width == nothing || (attribs = "width=$width")
(attribs != "" && height != nothing ) && (attribs *= ",")
height == nothing || (attribs *= "height=$height")
if f_env != nothing
result *= """\\begin{$f_env}[$f_pos]\n"""
end
for fig = fignames
if splitext(fig)[2] == ".tex" #Tikz figures
figstring *= "\\resizebox{$width}{!}{\\input{$fig}}\n"
else
figstring *= "\\includegraphics[$attribs]{$fig}\n"
end
end
# Figure environment
if caption != nothing
result *= string("\\center\n",
"$figstring",
"\\caption{$caption}\n")
else
result *= figstring
end
if chunk.options[:name] != nothing && f_env !=nothing
label = chunk.options[:name]
result *= "\\label{fig:$label}\n"
end
if f_env != nothing
result *= "\\end{$f_env}\n"
end
return result
end
function formatfigures(chunk, docformat::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 = ""
if f_env == nothing && caption != nothing
f_env = "figure"
end
f_pos == nothing && (f_pos = "!h")
#Set size
attribs = ""
width == nothing || (attribs = "width=$width")
(attribs != "" && height != nothing ) && (attribs *= ",")
height == nothing || (attribs *= "height=$height")
if f_env != nothing
result *= """\\begin{$f_env}[$f_pos]\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
# Figure environment
if caption != nothing
result *= string("\\center\n",
"$figstring",
"\\caption{$caption}\n")
else
result *= figstring
end
if chunk.options[:name] != nothing && f_env !=nothing
label = chunk.options[:name]
result *= "\\label{fig:$label}\n"
end
if f_env != nothing
result *= "\\end{$f_env}\n"
end
return result
end
function formatfigures(chunk, docformat::Pandoc)
fignames = chunk.figures
caption = chunk.options[:fig_cap]
result = ""
figstring = ""
attribs = ""
width = chunk.options[:out_width]
height = chunk.options[:out_height]
#Build figure attibutes
width == nothing || (attribs = "width=$width")
(attribs "" && height nothing ) && (attribs *= " ")
height == nothing || (attribs *= "height=$height")
attribs == "" || (attribs = "{$attribs}")
length(fignames) > 0 || (return "")
if caption != nothing
result *= "![$caption]($(fignames[1]))$attribs\n"
for fig = 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::Markdown)
fignames = chunk.figures
caption = chunk.options[:fig_cap]
result = ""
figstring = ""
length(fignames) > 0 || (return "")
if caption != nothing
result *= "![$caption]($(fignames[1]))\n"
for fig = 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 ? "" : ".."
function format_shortcode(index_and_fig)
index, fig = index_and_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
mapreduce(format_shortcode, *, "", enumerate(chunk.figures))
end
function formatfigures(chunk, docformat::MultiMarkdown)
fignames = chunk.figures
caption = chunk.options[:fig_cap]
result = ""
figstring = ""
if chunk.options[:out_width] == nothing
width = ""
else
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")
end
else
for fig in fignames
result *= "![][$fig]\n\n"
result *= "[$fig]: $fig $width\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=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]
width = chunk.options[:out_width]
result = ""
figstring = ""
for fig=fignames
figstring *= @sprintf("image::%s[width=%s]\n", fig, width)
end
if caption != nothing
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
)

View File

@ -1,51 +1,42 @@
module GadflyPlots
using ..Weave, ..Gadfly
import Gadfly
Gadfly.set_default_plot_format(:svg)
Base.showable(m::MIME"application/pdf", p::Gadfly.Plot) = true
Base.showable(m::MIME"application/png", p::Gadfly.Plot) = true
#Gadfly doesn't call the default display methods, this catches
#all Gadfly plots
function Base.display(report::Report, m::MIME"image/svg+xml", p::Gadfly.Plot)
function Base.display(report::Weave.Report, m::MIME"application/pdf", p::Gadfly.Plot)
display(report, MIME("image/svg+xml"), p)
end
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
function Base.display(report::Weave.Report, m::MIME"image/svg+xml", p::Gadfly.Plot)
chunk = report.cur_chunk
full_name, rel_name = get_figname(report, chunk)
w = chunk.options[:fig_width] * Gadfly.inch
h = chunk.options[:fig_height] * Gadfly.inch
docformat = formats[report.formatdict[:doctype]]
push!(report.figures, rel_name)
report.fignum += 1
w = chunk.options[:fig_width]Gadfly.inch
h = chunk.options[:fig_height]Gadfly.inch
format = chunk.options[:fig_ext]
dpi = chunk.options[:dpi]
full_name, rel_name = Weave.get_figname(report, chunk, ext = format)
push!(report.figures, rel_name)
report.fignum += 1
if format == ".svg"
Gadfly.draw(Gadfly.SVG(full_name, w, h), p)
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)
#This is probably not the correct way to handle different formats, but it works.
if format == ".png"
try
Gadfly.draw(Gadfly.PNG(full_name, w, h, dpi=dpi), p)
catch
Gadfly.draw(Gadfly.PNG(full_name, w, h), p) #Compose < 0.3.1, Gadfly < 0.3.1
end
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 == ".svg"
Gadfly.draw(Gadfly.SVG(full_name, w, h), p)
elseif format == ".js.svg"
Gadfly.draw(Gadfly.SVGJS(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")
warn("Can't save figure. Unsupported format")
end
end
end

123
src/pandoc.jl Normal file
View File

@ -0,0 +1,123 @@
"""
`pandoc2html(formatted::AbstractString, doc::WeaveDoc)`
Convert output from pandoc markdown to html using Weave.jl template
"""
function pandoc2html(formatted::AbstractString, doc::WeaveDoc, outname::AbstractString)
weavedir = dirname(@__FILE__)
html_template = joinpath(weavedir, "../templates/pandoc_skeleton.html")
css_template = joinpath(weavedir, "../templates/pandoc_skeleton.css")
path, wsource = splitdir(abspath(doc.source))
wversion = string(Pkg.installed("Weave"))
wtime = string(Date(now()))
#Header is inserted from displayed plots
header_script = doc.header_script
if header_script ""
self_contained = []
else
self_contained = "--self-contained"
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)
try
pandoc_out, pandoc_in, proc = readandwrite(`pandoc -R -s --mathjax="" --highlight-style=tango
$filt $citeproc
--template $html_template -H $css_template $self_contained
-V wversion=$wversion -V wtime=$wtime -V wsource=$wsource
-V headerscript=$header_script
-o $outname`)
println(pandoc_in, formatted)
close(pandoc_in)
proc_output = readstring(pandoc_out)
cd(old_wd)
catch e
cd(old_wd)
warn("Error converting document to HTML")
throw(e)
end
end
"""
`pandoc2pdf(formatted::AbstractString, doc::WeaveDoc)`
Convert output from pandoc markdown to pdf using Weave.jl template
"""
function pandoc2pdf(formatted::AbstractString, doc::WeaveDoc, outname::AbstractString)
weavedir = dirname(@__FILE__)
header_template = joinpath(weavedir, "../templates/pandoc_header.txt")
path, wsource = splitdir(abspath(doc.source))
wversion = string(Pkg.installed("Weave"))
wtime = Date(now())
outname = basename(outname)
#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
info("Done executing code. Running xelatex")
try
pandoc_out, pandoc_in, proc = readandwrite(`pandoc -R -s --latex-engine=xelatex --highlight-style=tango
$filt $citeproc
--include-in-header=$header_template
-V fontsize=12pt -o $outname`)
println(pandoc_in, formatted)
close(pandoc_in)
proc_output = readstring(pandoc_out)
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 = "pdflatex")
old_wd = pwd()
cd(doc.cwd)
xname = basename(outname)
info("Weaved code to $outname. Running $latex_cmd")
try
textmp = mktempdir(".")
out = readstring(`$latex_cmd --output-directory=$textmp $xname`)
pdf = joinpath(textmp, "$(doc.basename).pdf")
cp(pdf, "$(doc.basename).pdf", remove_destination=true)
rm(textmp, recursive=true)
rm(xname)
cd(old_wd)
return true
catch e
cd(old_wd)
warn("Error converting document to pdf. Try running latex manually")
return false
#throw(e)
end
end

View File

@ -1,112 +1,75 @@
module WeavePlots
import Plots
using ..Base64, ..Plots, ..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))
"""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
end
Weave.push_preexecution_hook!(plots_set_size!)
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::Report, m::MIME"image/svg+xml", data::Plots.Plot{Plots.PlotlyBackend})#
#Remove extra spaces from start of line for pandoc
s = reprmime(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::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)
if report.first_plot
report.header_script *= "<script src=\"https://cdn.plot.ly/plotly-latest.min.js\"></script>"
report.first_plot = false
end
#PNG or SVG is not working, output html
function Base.display(report::Report, m::MIME"image/svg+xml", plot::Plots.Plot{Plots.PlotlyJSBackend})
body = Plots.PlotlyJS.html_body(plot.o.plot)
report.rich_output *= "\n" * body
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
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::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)
function add_plots_figure(report::Report, plot::Plots.Plot, ext)
chunk = report.cur_chunk
full_name, rel_name = 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)
function Base.display(report::Report, m::MIME"application/pdf", plot::Plots.Plot)
add_plots_figure(report, plot, ".pdf")
end
function Base.display(report::Weave.Report, m::MIME"image/png", plot::Plots.Plot)
function Base.display(report::Report, m::MIME"image/png", plot::Plots.Plot)
add_plots_figure(report, plot, ".png")
end
function Base.display(report::Weave.Report, m::MIME"image/svg+xml", plot::Plots.Plot)
function Base.display(report::Report, m::MIME"image/svg+xml", plot::Plots.Plot)
add_plots_figure(report, plot, ".svg")
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
report.rich_output *= "\n" * res * "\n"
end
end

20
src/pyplot.jl Normal file
View File

@ -0,0 +1,20 @@
import PyPlot
function savefigs_pyplot(report::Report)
chunk = report.cur_chunk
fignames = AbstractString[]
ext = report.formatdict[:fig_ext]
figpath = joinpath(report.cwd, chunk.options[:fig_path])
isdir(figpath) || mkdir(figpath)
chunkid = (chunk.options[:name] == nothing) ? chunk.number : chunk.options[:name]
#Iterate over all open figures, save them and store names
for fig = PyPlot.plt[:get_fignums]()
full_name, rel_name = get_figname(report, chunk, fignum=fig)
PyPlot.savefig(full_name, dpi=chunk.options[:dpi])
push!(report.figures, rel_name)
report.fignum += 1
PyPlot.plt[:draw]()
PyPlot.plt[:close]()
end
end

View File

@ -1,95 +0,0 @@
function parse_markdown(document_body; is_pandoc = false)
header_text, document_body, offset = separate_header_text(document_body)
header = parse_header(header_text)
code_start, code_end = if is_pandoc
r"^<<(?<options>.*?)>>=\s*$",
r"^@\s*$"
else
r"^[`~]{3}(\{?)julia\s*([;,\{]?)\s*(?<options>.*?)(\}|\s*)$",
r"^[`~]{3}\s*$"
end
return header, parse_markdown_body(document_body, code_start, code_end, offset)
end
# headers
# -------
const HEADER_REGEX = r"^---$(?<header>((?!---).)+)^---$"ms
# TODO: non-Weave headers should keep live in a doc
# separates header section from `text`
function separate_header_text(text)
m = match(HEADER_REGEX, text)
isnothing(m) && return "", text, 0
header_text = m[:header]
offset = @static if VERSION v"1.4"
count("\n", header_text)
else
count(c->c==='\n', header_text)
end
return header_text, replace(text, HEADER_REGEX => ""; count = 1), offset
end
# HACK:
# YAML.jl can't parse text including ``` characters, so first replace all the inline code
# with these temporary code start/end string
const HEADER_INLINE_START = "<weave_header_inline_start>"
const HEADER_INLINE_END = "<weave_header_inline_end>"
function parse_header(header_text)
isempty(header_text) && return Dict()
pat = INLINE_REGEX => SubstitutionString("$(HEADER_INLINE_START)\\1$(HEADER_INLINE_END)")
header_text = replace(header_text, pat)
return YAML.load(header_text)
end
# body
# ----
function parse_markdown_body(document_body, code_start, code_end, offset)
lines = split(document_body, '\n')
state = :doc
doc_no = 0
code_no = 0
content = ""
start_line = offset
options = OptionDict()
option_string = ""
chunks = WeaveChunk[]
for (line_no, line) in enumerate(lines)
m = match(code_start, line)
if !isnothing(m) && state === :doc
state = :code
option_string = isnothing(m[:options]) ? "" : strip(m[:options])
options = parse_options(option_string)
haskey(options, :label) && (options[:name] = options[:label])
haskey(options, :name) || (options[:name] = nothing)
isempty(strip(content)) || push!(chunks, DocChunk(content, doc_no += 1, start_line))
start_line = line_no + offset
content = ""
continue
end
if occursin(code_end, line) && state === :code
push!(chunks, CodeChunk(content, code_no += 1, start_line, option_string, options))
start_line = line_no + offset
content = ""
state = :doc
continue
end
content *= isone(line_no) ? line : string('\n', line)
end
# Remember the last chunk
isempty(strip(content)) || push!(chunks, DocChunk(content, doc_no += 1, start_line))
return chunks
end

View File

@ -1,23 +0,0 @@
using JSON
function parse_notebook(document_body)
nb = JSON.parse(document_body)
code_no = 0
doc_no = 0
# TODO: handle some of options ?
options = Dict{Symbol,Any}()
opt_string = ""
chunks = map(nb["cells"]) do cell
text = string('\n', join(cell["source"]), '\n')
return if cell["cell_type"] == "code"
CodeChunk(text, code_no += 1, 0, opt_string, options)
else
DocChunk(text, doc_no += 1, 0; notebook = true)
end
end
return Dict(), chunks
end

View File

@ -1,128 +0,0 @@
using YAML
function WeaveDoc(source, informat = nothing)
path = abspath(source)
_, fname = splitdir(path)
basename = splitext(fname)[1]
isnothing(informat) && (informat = detect_informat(source))
header, chunks = parse_doc(read(source, String), informat)
# update default chunk options from header
chunk_defaults = deepcopy(get_chunk_defaults())
if (weave_options = get(header, WEAVE_OPTION_NAME, nothing)) !== nothing
for key in keys(chunk_defaults)
if (val = get(weave_options, string(key), nothing)) !== nothing
chunk_defaults[key] = val
end
end
end
if haskey(header, WEAVE_OPTION_NAME_DEPRECATED)
@warn "Weave: `options` key is deprecated. Use `weave_options` key instead." _id = WEAVE_OPTION_DEPRECATE_ID maxlog = 1
for key in keys(chunk_defaults)
if (val = get(header[WEAVE_OPTION_NAME_DEPRECATED], string(key), nothing)) !== nothing
chunk_defaults[key] = val
end
end
end
return WeaveDoc(
source,
basename,
path,
chunks,
"",
nothing,
"",
"",
header,
chunk_defaults,
)
end
"""
detect_informat(path)
Detect Weave input format based on file extension of `path`.
"""
function detect_informat(path)
ext = lowercase(last(splitext(path)))
ext == ".jl" && return "script"
ext == ".jmd" && return "markdown"
ext == ".ipynb" && return "notebook"
return "noweb"
end
function parse_doc(document, informat)
document = replace(document, "\r\n" => "\n") # normalize line ending
return informat == "markdown" ? parse_markdown(document) :
informat == "noweb" ? parse_markdown(document; is_pandoc = true) :
informat == "script" ? parse_script(document) :
informat == "notebook" ? parse_notebook(document) :
error("unsupported input format given: $informat")
end
# inline
# ------
function DocChunk(text::AbstractString, number, start_line; notebook = false)
# don't parse inline code in notebook
content = notebook ? parse_inline(text) : parse_inlines(text)
return DocChunk(content, number, start_line)
end
const INLINE_REGEX = r"`j\s+(.*?)`"
const INLINE_REGEXES = r"`j\s+(.*?)`|^!\s(.*)$"m
# handle code units correctly !
function parse_inlines(str)
ret = Inline[]
s = 1
code_no = text_no = 0
for m in eachmatch(INLINE_REGEXES, str)
e = m.offset
push!(ret, InlineText((str[s:prevind(str,e)]), text_no += 1))
i = findfirst(!isnothing, m.captures)
push!(ret, InlineCode(m.captures[i], code_no += 1, isone(i) ? :inline : :line))
s = e + ncodeunits(m.match)
end
push!(ret, InlineText(str[s:end], text_no += 1))
return ret
end
parse_inline(str) = Inline[InlineText(str, 1)]
# options
# -------
const OptionDict = Dict{Symbol,Any}
function parse_options(str)::OptionDict
str = string('(', str, ')')
ex = Meta.parse(str)
nt = if Meta.isexpr(ex, (
:block, # "(k1 = v1; k2 = v2, ...)"
:tuple, # "(k1 = v1, k2 = v2, ...)"
))
eval(Expr(:tuple, filter(is_valid_kv, ex.args)...))
elseif is_valid_kv(ex) # "(k = v)"
eval(Expr(:tuple, ex))
else
NamedTuple{}()
end
return dict(nt)
end
is_valid_kv(x) = Meta.isexpr(x, :(=))
dict(nt) = Dict((k => v for (k,v) in zip(keys(nt), values(nt)))...)
nt(dict) = NamedTuple{(Symbol.(keys(dict))...,)}((collect(values(dict))...,))
# each input format
# -----------------
include("markdown.jl")
include("script.jl")
include("notebook.jl")

View File

@ -1,67 +0,0 @@
function parse_script(document_body)
lines = split(document_body, '\n')
doc_line = r"(^#'.*)|(^#%%.*)|(^# %%.*)"
doc_start = r"(^#')|(^#%%)|(^# %%)"
opt_line = r"(^#\+.*$)|(^#%%\+.*$)|(^# %%\+.*$)"
opt_start = r"(^#\+)|(^#%%\+)|(^# %%\+)"
content = ""
state = :code
doc_no = 0
code_no = 0
start_line = 1
options = OptionDict()
option_string = ""
chunks = WeaveChunk[]
for (line_no, line) in enumerate(lines)
if (m = match(doc_line, line)) !== nothing && (m = match(opt_line, line)) === nothing
line = replace(line, doc_start => "", count = 1)
startswith(line, ' ') && (line = replace(line, ' ' => "", count = 1))
if state === :code && !isempty(strip(content))
push!(chunks, CodeChunk(string('\n', strip(content)), code_no += 1, start_line, option_string, options))
content = ""
start_line = line_no
end
state = :doc
elseif (m = match(opt_line, line)) !== nothing
start_line = line_no
if state === :code && !isempty(strip(content))
push!(chunks, CodeChunk(string('\n', strip(content)), code_no += 1, start_line, option_string, options))
content = ""
end
if state === :doc && !isempty(strip(content))
iszero(doc_no) || (content = string('\n', content)) # Add whitespace to doc chunk. Needed for markdown output
push!(chunks, DocChunk(content, doc_no += 1, start_line))
content = ""
end
option_string = replace(line, opt_start => "", count = 1)
options = parse_options(option_string)
haskey(options, :label) && (options[:name] = options[:label])
haskey(options, :name) || (options[:name] = nothing)
state = :code
continue
elseif state === :doc # && strip(line) != "" && strip(content) != ""
state = :code
iszero(doc_no) || (content = string('\n', content)) # Add whitespace to doc chunk. Needed for markdown output
push!(chunks, DocChunk(content, doc_no += 1, start_line))
content = ""
options = Dict{Symbol,Any}()
start_line = line_no
end
content *= string(line, '\n')
end
# Handle the last chunk
chunk = state === :code ?
CodeChunk(string('\n', strip(content)), code_no, start_line, option_string, options) :
DocChunk(content, doc_no, start_line)
push!(chunks, chunk)
return Dict(), chunks
end

313
src/readers.jl Normal file
View File

@ -0,0 +1,313 @@
import JSON, YAML
pushopt(options::Dict,expr::Expr) = Base.Meta.isexpr(expr,:(=)) && (options[expr.args[1]] = expr.args[2])
type MarkupInput
codestart::Regex
codeend::Regex
inline::Regex
end
type ScriptInput
doc_line::Regex
doc_start::Regex
opt_line::Regex
opt_start::Regex
inline::Regex
end
type NotebookInput
inline
end
const input_formats = Dict{AbstractString, Any}(
"noweb" => MarkupInput(r"^<<(.*?)>>=\s*$",
r"^@\s*$",
r"`j\s+(.*?)`"s
),
"markdown" => MarkupInput(
r"^[`~]{3,}(?:\{|\{\.|)julia(?:;|)\s*(.*?)(\}|\s*)$",
r"^[`~]{3,}\s*$",
r"`j\s+(.*?)`"s),
"script" => ScriptInput(
r"(^#'.*)|(^#%%.*)|(^# %%.*)",
r"(^#')|(^#%%)|(^# %%)",
r"(^#\+.*$)|(^#%%\+.*$)|(^# %%\+.*$)",
r"(^#\+)|(^#%%\+)|(^# %%\+)",
r"`j\s+(.*?)`"s),
"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 == ".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)
format == :auto && (format = detect_informat(source))
document = readstring(source)
document = replace(document, "\r\n", "\n")
parsed = parse_doc(document, format)
header = parse_header(parsed[1])
doc = WeaveDoc(source, parsed, header)
return doc
end
function parse_header(chunk::CodeChunk)
return Dict()
end
function parse_header(chunk::DocChunk)
m = match(r"^---$(?<header>.+)^---$"ms, 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])
end
"""Parse documents with Weave.jl markup"""
function parse_doc(document::AbstractString, format::MarkupInput)
document = replace(document, "\r\n", "\n")
lines = split(document, "\n")
codestart = format.codestart
codeend = format.codeend
state = "doc"
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{Symbol,Any}()
if length(optionString) > 0
expr = 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))
chunk = DocChunk(content, docno, start_line, format.inline)
docno += 1
push!(parsed, chunk)
end
content = ""
start_line = lineno
continue
end
if ismatch(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
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")
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
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, "", 1)
if startswith(line, " ")
line = replace(line, " ", "", 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, "", 1)
#Get options
options = Dict{Symbol,Any}()
if length(optionString) > 0
expr = 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
end
read *= line * "\n"
if strip(line) == ""
n_emptylines += 1
else
n_emptylines = 0
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
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
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
end
end
return parsed
end
#Use this if regex is undefined
function parse_inline(text, noex)
return Inline[InlineText(text, 1, length(text), 1)]
end
function parse_inline(text::AbstractString, inline_ex::Regex)
ismatch(inline_ex, text) || return Inline[InlineText(text, 1, length(text), 1)]
inline_chunks = eachmatch(inline_ex, text)
s = 1
e = 1
res = Inline[]
textno = 1
codeno = 1
for ic in inline_chunks
s = ic.offset
doc = InlineText(text[e:(s-1)], e, s-1, textno)
textno += 1
push!(res, doc)
e = s + length(ic.match)
push!(res, InlineCode(ic.captures[1], s, e, codeno))
codeno += 1
end
push!(res, InlineText(text[e:end], e, length(text), textno))
return res
end

View File

@ -1,162 +0,0 @@
# fallback methods
# ----------------
set_format_options!(docformat::WeaveFormat; _kwargs...) = return
function restore_header!(doc)
(hasproperty(doc.format, :preserve_header) && doc.format.preserve_header) || return
# only strips Weave headers
delete!(doc.header, WEAVE_OPTION_NAME)
if haskey(doc.header, WEAVE_OPTION_NAME_DEPRECATED)
@warn "Weave: `options` key is deprecated. Use `weave_options` key instead." _id = WEAVE_OPTION_DEPRECATE_ID maxlog = 1
delete!(doc.header, WEAVE_OPTION_NAME_DEPRECATED)
end
isempty(doc.header) && return
# restore remained headers as `DocChunk`
header_text = "---\n$(YAML.write(doc.header))---"
pushfirst!(doc.chunks, DocChunk(header_text, 0, 0))
end
render_chunk(docformat::WeaveFormat, chunk::DocChunk) = join((render_inline(c) for c in chunk.content))
render_inline(inline::InlineText) = inline.content
function render_inline(inline::InlineCode)
isempty(inline.rich_output) || return inline.rich_output
isempty(inline.figures) || return inline.figures[end]
return inline.output
end
function render_chunk(docformat::WeaveFormat, chunk::CodeChunk)
# Fill undefined options with format specific defaults
isnothing(chunk.options[:out_width]) && (chunk.options[:out_width] = docformat.out_width)
isnothing(chunk.options[:fig_pos]) && (chunk.options[:fig_pos] = docformat.fig_pos)
# Only use floats if chunk has caption or sets fig_env
if !isnothing(chunk.options[:fig_cap]) && isnothing(chunk.options[:fig_env])
(chunk.options[:fig_env] = docformat.fig_env)
end
hasproperty(docformat, :indent) && (chunk.content = indent(chunk.content, docformat.indent))
chunk.content = render_code(docformat, chunk.content)
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 echo
# Convert to output format and highlight (html, tex...) if needed
string(docformat.codestart, chunk.content, docformat.codeend, '\n')
else
""
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 = render_output(docformat, chunk.output)
else
chunk.output = '\n' * rstrip(chunk.output)
chunk.output = render_output(docformat, chunk.output)
end
hasproperty(docformat, :indent) && (chunk.output = indent(chunk.output, docformat.indent))
strip(chunk.output) "" && (
result *= "$(docformat.outputstart)$(chunk.output)\n$(docformat.outputend)\n"
)
strip(chunk.rich_output) "" && (result *= chunk.rich_output * '\n')
end
end
end
# Handle figures
if chunk.options[:fig] && length(chunk.figures) > 0
result *= render_figures(docformat, chunk)
end
return result
end
render_code(docformat::WeaveFormat, code) = code
indent(text, nindent) = join(map(x -> string(repeat(' ', nindent), x), split(text, '\n')), '\n')
function wraplines(text, line_width = 75)
result = AbstractString[]
lines = split(text, '\n')
for line in lines
if length(line) > line_width
push!(result, wrapline(line, line_width))
else
push!(result, line)
end
end
return strip(join(result, '\n'))
end
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)
end
result *= text
end
render_output(docformat::WeaveFormat, output) = output
function render_termchunk(docformat::WeaveFormat, chunk)
return if should_render(chunk)
string(docformat.termstart, chunk.output, docformat.termend)
else
""
end
end
should_render(chunk) = chunk.options[:echo] && chunk.options[:results] "hidden"
render_doc(docformat, body, doc) = 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_mustache_template(path::AbstractString) = Mustache.template_from_file(path)
get_mustache_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,24 +0,0 @@
abstract type ExportFormat <: WeaveFormat end
function Base.getproperty(sf::T, s::Symbol) where {T<:ExportFormat}
hasfield(T, s) && return getfield(sf, s)
return getproperty(sf.primaryformat, s)
end
function Base.setproperty!(sf::T, s::Symbol, v) where {T<:ExportFormat}
if hasfield(T, s)
setfield!(sf, s, v)
else
setproperty!(sf.primaryformat, s, v)
end
end
function Base.hasproperty(sf::T, s::Symbol) where {T<:ExportFormat}
hasfield(T, s) || hasproperty(sf.primaryformat, s)
end
render_doc(df::ExportFormat, body, doc) = render_doc(df.primaryformat, body, doc)
render_chunk(df::ExportFormat, chunk) = render_chunk(df.primaryformat, chunk)
# Need to define these to avoid ambiguities
render_chunk(df::ExportFormat, chunk::DocChunk) = render_chunk(df.primaryformat, chunk)
render_chunk(df::ExportFormat, chunk::CodeChunk) = render_chunk(df.primaryformat, chunk)
render_output(df::ExportFormat, output) = render_output(df.primaryformat, output)

View File

@ -1,146 +0,0 @@
# HTML
# ----
abstract type HTMLFormat <: WeaveFormat end
render_code(docformat::HTMLFormat, code) =
highlight_code(MIME("text/html"), code, docformat.highlight_theme)
render_termchunk(docformat::HTMLFormat, chunk) =
should_render(chunk) ? highlight_term(MIME("text/html"), chunk.output, docformat.highlight_theme) : ""
# Julia markdown
# --------------
Base.@kwdef mutable struct WeaveHTML <: HTMLFormat
description = "Weave-style HTML"
extension = "html"
codestart = '\n'
codeend = '\n'
termstart = codestart
termend = codeend
outputstart = "<pre class=\"output\">"
outputend = "</pre>\n"
mimetypes = ["image/png", "image/jpg", "image/svg+xml",
"text/html", "text/markdown", "text/plain"]
fig_ext = ".png"
out_width = nothing
out_height = nothing
fig_pos = nothing
fig_env = nothing
# specials
template = nothing
stylesheet = nothing
highlight_theme = nothing
end
register_format!("md2html", WeaveHTML())
function set_format_options!(docformat::WeaveHTML; template = nothing, css = nothing, highlight_theme = nothing, _kwargs...)
template_path = isnothing(template) ? normpath(TEMPLATE_DIR, "md2html.tpl") : template
docformat.template = get_mustache_template(template_path)
stylesheet_path = isnothing(css) ? normpath(STYLESHEET_DIR, "skeleton.css") : css
docformat.stylesheet = read(stylesheet_path, String)
docformat.highlight_theme = get_highlight_theme(highlight_theme)
end
# very similar to tex version of function
function render_chunk(docformat::WeaveHTML, chunk::DocChunk)
out = IOBuffer()
io = IOBuffer()
for inline in chunk.content
if isa(inline, InlineText)
write(io, inline.content)
elseif !isempty(inline.rich_output)
clear_buffer_and_format!(io, out, WeaveMarkdown.html)
write(out, addlines(inline.rich_output, inline))
elseif !isempty(inline.figures)
write(io, inline.figures[end])
elseif !isempty(inline.output)
write(io, addlines(inline.output, inline))
end
end
clear_buffer_and_format!(io, out, WeaveMarkdown.html)
return take2string!(out)
end
render_output(docformat::WeaveHTML, output) = Markdown.htmlesc(output)
function render_figures(docformat::WeaveHTML, chunk)
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 = ""
# Set size
attribs = ""
isnothing(width) || (attribs = "width=\"$width\"")
(!isempty(attribs) && !isnothing(height)) && (attribs *= ",")
isnothing(height) || (attribs *= " height=\"$height\" ")
if !isnothing(caption)
result *= """<figure>\n"""
end
for fig in fignames
figstring *= """<img src="$fig" $attribs />\n"""
end
result *= figstring
if !isnothing(caption)
result *= """
<figcaption>$caption</figcaption>
"""
end
if !isnothing(caption)
result *= "</figure>\n"
end
return result
end
function render_doc(docformat::WeaveHTML, body, doc; css = nothing)
_, weave_source = splitdir(abspath(doc.source))
weave_version, weave_date = weave_info()
return Mustache.render(
docformat.template;
body = body,
stylesheet = docformat.stylesheet,
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
# Pandoc
# ------
Base.@kwdef mutable struct Pandoc2HTML <: ExportFormat
description = "HTML via intermediate Pandoc Markdown (requires Pandoc 2)"
primaryformat = Pandoc()
template_path = nothing
stylesheet_path = nothing
highlight_theme = nothing
pandoc_options = String[]
end
register_format!("pandoc2html", Pandoc2HTML())
function set_format_options!(docformat::Pandoc2HTML; template = nothing, css = nothing, highlight_theme = nothing, pandoc_options = String[], _kwargs...)
docformat.template_path =
isnothing(template) ? normpath(TEMPLATE_DIR, "pandoc2html.html") : template
docformat.stylesheet_path =
isnothing(css) ? normpath(STYLESHEET_DIR, "pandoc2html_skeleton.css") : css
docformat.highlight_theme = get_highlight_theme(highlight_theme)
docformat.pandoc_options = pandoc_options
end
render_figures(docformat::Pandoc2HTML, chunk) = render_figures(Pandoc(), chunk)

View File

@ -1,224 +0,0 @@
# GitHub markdown
# ---------------
Base.@kwdef mutable struct GitHubMarkdown <: WeaveFormat
description = "GitHub Markdown"
extension = "md"
codestart = "```julia"
codeend = "```\n"
termstart = codestart
termend = codeend
outputstart = "```"
outputend = "```\n\n"
fig_ext = ".png"
mimetypes = ["image/png", "image/svg+xml", "image/jpg",
"text/markdown", "text/plain"]
out_width = nothing
out_height = nothing
fig_pos = nothing
fig_env = nothing
# specials
preserve_header = true
end
register_format!("github", GitHubMarkdown())
function render_figures(docformat::GitHubMarkdown, chunk)
fignames = chunk.figures
caption = chunk.options[:fig_cap]
result = ""
figstring = ""
length(fignames) > 0 || (return "")
if !isnothing(caption)
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 <: WeaveFormat
description = "Hugo Markdown (using shortcodes)"
extension = "md"
codestart = "```julia"
codeend = "```\n"
termstart = codestart
termend = codeend
outputstart = "```"
outputend = "```\n\n"
mimetypes = default_mime_types
fig_ext = ".png"
out_width = nothing
out_height = nothing
fig_pos = nothing
fig_env = nothing
# specials
preserve_header = true
uglyURLs = false # if `false`, prepend figure path by `..`
end
register_format!("hugo", Hugo())
function render_figures(docformat::Hugo, chunk)
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 = isnothing(caption) ? "" : "title=\"$(caption)\" "
end
"{{< figure src=\"$(joinpath(relpath, fig))\" $(title_spec) >}}"
end
end
# multi language markdown
# -----------------------
Base.@kwdef mutable struct MultiMarkdown <: WeaveFormat
description = "MultiMarkdown"
extension = "md"
codestart = "```julia"
codeend = "```\n"
termstart = codestart
termend = codeend
outputstart = "```"
outputend = "```\n\n"
mimetypes = default_mime_types
fig_ext = ".png"
out_width = nothing
out_height = nothing
fig_pos = nothing
fig_env = nothing
# specials
preserve_header = true
end
register_format!("multimarkdown", MultiMarkdown())
function render_figures(docformat::MultiMarkdown, chunk)
fignames = chunk.figures
caption = chunk.options[:fig_cap]
result = ""
figstring = ""
if chunk.options[:out_width] == nothing
width = ""
else
width = "width=$(chunk.options[:out_width])"
end
length(fignames) > 0 || (return "")
if !isnothing(caption)
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"
end
end
return result
end
# Rest
# ----
Base.@kwdef mutable struct Rest <: WeaveFormat
description = "reStructuredText and Sphinx"
extension = "rst"
codestart = ".. code-block:: julia\n"
codeend = "\n"
termstart = codestart
termend = codeend
outputstart = "::\n"
outputend = "\n\n"
mimetypes = default_mime_types
fig_ext = ".png"
out_width = "15 cm"
out_height = nothing
fig_pos = nothing
fig_env = nothing
# specials
indent = 4
end
register_format!("rst", Rest())
function render_figures(docformat::Rest, chunk)
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 !isnothing(caption)
result *= string(
".. figure:: $(fignames[1])\n",
" :width: $width\n\n",
" $caption\n\n",
)
else
result *= figstring
return result
end
end
# Ansii
# -----
# asciidoc -b html5 -a source-highlighter=pygments ...
Base.@kwdef mutable struct AsciiDoc <: WeaveFormat
description = "AsciiDoc"
extension = "txt"
codestart = "[source,julia]\n--------------------------------------"
codeend = "--------------------------------------\n"
termstart = codestart
termend = codeend
outputstart = "--------------------------------------"
outputend = "--------------------------------------\n\n"
mimetypes = default_mime_types
fig_ext = ".png"
out_width = "600"
out_height = nothing
fig_pos = nothing
fig_env = nothing
end
register_format!("asciidoc", AsciiDoc())
function render_figures(docformat::AsciiDoc, chunk)
fignames = chunk.figures
caption = chunk.options[:fig_cap]
width = chunk.options[:out_width]
result = ""
figstring = ""
for fig in fignames
figstring *= @sprintf("image::%s[width=%s]\n", fig, width)
end
if !isnothing(caption)
result *= string("image::$(fignames[1])", "[width=$width,", "title=\"$caption\"]")
else
result *= figstring
return result
end
end

View File

@ -1,70 +0,0 @@
abstract type PandocFormat <: WeaveFormat end
function render_figures(docformat::PandocFormat, chunk)
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[]
isnothing(width) || push!(attribs, "width=$width")
isnothing(height) || push!(attribs, "height=$height")
isnothing(label) || push!(attribs, "#fig:$label")
attribs = isempty(attribs) ? "" : "{" * join(attribs, " ") * "}"
if !isnothing(caption)
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
Base.@kwdef mutable struct Pandoc <: PandocFormat
description = "Pandoc Markdown"
extension = "md"
codestart = "~~~~{.julia}"
codeend = "~~~~~~~~~~~~~\n"
termstart = codestart
termend = codeend
outputstart = "~~~~"
outputend = "~~~~\n\n"
# Prefer png figures for markdown conversion, svg doesn't work with latex
mimetypes = ["image/png", "image/jpg", "image/svg+xml", "text/markdown", "text/plain"]
fig_ext = ".png"
out_width = nothing
out_height = nothing
fig_pos = nothing
fig_env = nothing
# specials
preserve_header = true
end
register_format!("pandoc", Pandoc())
const DEFAULT_PANDOC_OPTIONS = String[]
Base.@kwdef mutable struct Pandoc2PDF <: ExportFormat
description = "PDF via intermediate Pandoc Markdown"
primaryformat = Pandoc()
pandoc_options = DEFAULT_PANDOC_OPTIONS
header_template = normpath(TEMPLATE_DIR, "pandoc2pdf_header.txt")
end
register_format!("pandoc2pdf", Pandoc2PDF())
function set_format_options!(docformat::Pandoc2PDF; pandoc_options = DEFAULT_PANDOC_OPTIONS, _kwargs...)
docformat.pandoc_options = pandoc_options
end

View File

@ -1,31 +0,0 @@
# TODO:
# - 1. Improve argument handling
# - 2. Update code to use UnPack.jl to make it more readable
# - 3. Export new interface
# - 4. Document Interface
using Mustache, Highlights, .WeaveMarkdown, Markdown, Dates, Printf
const FORMATS = Dict{String,WeaveFormat}()
# TODO: do some assertion for necessary fields of `format`
register_format!(format_name::AbstractString, format::WeaveFormat) = push!(FORMATS, format_name => format)
register_format!(_, format) = error("Format needs to be a subtype of WeaveFormat.")
set_format_options!(doc; kwargs...) = set_format_options!(doc.format; kwargs...)
function render_doc(doc::WeaveDoc)
restore_header!(doc)
docformat = doc.format
body = joinlines(render_chunk.(Ref(docformat), copy(doc.chunks)))
return render_doc(docformat, body, doc)
end
include("exportformat.jl")
include("common.jl")
include("pandocformats.jl")
include("htmlformats.jl")
include("texformats.jl")
include("miscformats.jl")

View File

@ -1,264 +0,0 @@
# Tex
# ---
abstract type LaTeXFormat <: WeaveFormat end
function set_format_options!(docformat::LaTeXFormat; keep_unicode = false, template = nothing, _kwargs...)
docformat.keep_unicode |= keep_unicode
docformat.template =
get_mustache_template(isnothing(template) ? normpath(TEMPLATE_DIR, "md2pdf.tpl") : template)
end
# very similar to export to html
function render_chunk(docformat::LaTeXFormat, chunk::DocChunk)
out = IOBuffer()
io = IOBuffer()
for inline in chunk.content
if isa(inline, InlineText)
write(io, inline.content)
elseif !isempty(inline.rich_output)
clear_buffer_and_format!(io, out, WeaveMarkdown.latex)
write(out, addlines(inline.rich_output, inline))
elseif !isempty(inline.figures)
write(io, inline.figures[end], inline)
elseif !isempty(inline.output)
write(io, addlines(inline.output, inline))
end
end
clear_buffer_and_format!(io, out, WeaveMarkdown.latex)
out = take2string!(out)
return unicode2latex(docformat, out)
end
render_output(docformat::LaTeXFormat, output) = unicode2latex(docformat, output, true)
render_code(docformat::LaTeXFormat, code) = unicode2latex(docformat, code, true)
render_termchunk(docformat::LaTeXFormat, chunk) =
string(docformat.termstart,
unicode2latex(docformat, chunk.output, true),
docformat.termend, "\n")
# from julia symbols (e.g. "\bfhoge") to valid latex
const UNICODE2LATEX = let
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
Dict(unicode => texify(sym) for (sym, unicode) in REPL.REPLCompletions.latex_symbols)
end
function unicode2latex(docformat::LaTeXFormat, s, escape = false)
# Check whether to convert at all and return input if not
docformat.keep_unicode && return s
for (unicode, latex) in UNICODE2LATEX
body = "\\ensuremath{$(latex)}"
target = escape ? string(docformat.escape_starter, body, docformat.escape_closer) : body
s = replace(s, unicode => target)
end
return s
end
function render_figures(docformat::LaTeXFormat, chunk)
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 isnothing(f_env) && !isnothing(caption)
f_env = "figure"
end
(isnothing(f_pos)) && (f_pos = "!h")
# Set size
attribs = ""
isnothing(width) || (attribs = "width=$(md_length_to_latex(width,"\\linewidth"))")
(!isempty(attribs) && !isnothing(height)) && (attribs *= ",")
isnothing(height) || (attribs *= "height=$(md_length_to_latex(height,"\\paperheight"))")
if !isnothing(f_env)
result *= "\\begin{$f_env}"
(!isempty(f_pos)) && (result *= "[$f_pos]")
result *= "\n"
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 !isnothing(caption)
result *= string("\\center\n", "$figstring", "\\caption{$caption}\n")
else
result *= figstring
end
if !isnothing(chunk.options[:label]) && !isnothing(f_env)
label = chunk.options[:label]
result *= "\\label{fig:$label}\n"
end
if !isnothing(f_env)
result *= "\\end{$f_env}\n"
end
return result
end
function md_length_to_latex(def, reference)
if occursin("%", def)
_def = tryparse(Float64, replace(def, "%" => ""))
isnothing(_def) && return def
perc = round(_def / 100, digits = 2)
return "$perc$reference"
end
return def
end
function render_doc(docformat::LaTeXFormat, body, doc)
return Mustache.render(
docformat.template;
body = body,
highlight = "",
tex_deps = docformat.tex_deps,
[Pair(Symbol(k), v) for (k, v) in doc.header]...,
)
end
# minted Tex
# ----------
Base.@kwdef mutable struct LaTeXMinted <: LaTeXFormat
description = "LaTeX using minted package for code highlighting"
extension = "tex"
codestart = "\\begin{minted}[texcomments = true, mathescape, fontsize=\\small, xleftmargin=0.5em]{julia}"
codeend = "\\end{minted}"
termstart = "\\begin{minted}[texcomments = true, mathescape, fontsize=\\footnotesize, xleftmargin=0.5em]{jlcon}"
termend = "\\end{minted}"
outputstart = "\\begin{minted}[texcomments = true, mathescape, fontsize=\\small, xleftmargin=0.5em, frame = leftline]{text}"
outputend = "\\end{minted}"
mimetypes = ["application/pdf", "image/png", "text/latex", "text/plain"]
fig_ext = ".pdf"
out_width = "\\linewidth"
out_height = nothing
fig_pos = "htpb"
fig_env = "figure"
# specials
keep_unicode = false
template = nothing
tex_deps = "\\usepackage{minted}"
# how to escape latex in verbatim/code environment
escape_starter = "|\$"
escape_closer = reverse(escape_starter)
end
register_format!("texminted", LaTeXMinted())
# Tex (directly to PDF)
# ---------------------
abstract type WeaveLaTeXFormat <: LaTeXFormat end
function set_format_options!(docformat::WeaveLaTeXFormat; template = nothing, highlight_theme = nothing, keep_unicode = false, _kwargs...)
docformat.template =
get_mustache_template(isnothing(template) ? normpath(TEMPLATE_DIR, "md2pdf.tpl") : template)
docformat.highlight_theme = get_highlight_theme(highlight_theme)
docformat.keep_unicode |= keep_unicode
end
function render_output(docformat::WeaveLaTeXFormat, output)
# Highligts has some extra escaping defined, eg of $, ", ...
output_escaped = sprint(
(io, x) ->
Highlights.Format.escape(io, MIME("text/latex"), x, charescape = true),
output,
)
return unicode2latex(docformat, output_escaped, true)
end
function render_code(docformat::WeaveLaTeXFormat, code)
ret = highlight_code(MIME("text/latex"), code, docformat.highlight_theme)
unicode2latex(docformat, ret, false)
end
function render_termchunk(docformat::WeaveLaTeXFormat, chunk)
if should_render(chunk)
ret = highlight_term(MIME("text/latex"), chunk.output, docformat.highlight_theme)
unicode2latex(docformat, ret, true)
else
""
end
end
function render_doc(docformat::WeaveLaTeXFormat, body, doc)
return Mustache.render(
docformat.template;
body = body,
highlight = get_highlight_stylesheet(MIME("text/latex"), docformat.highlight_theme),
tex_deps = docformat.tex_deps,
[Pair(Symbol(k), v) for (k, v) in doc.header]...,
)
end
Base.@kwdef mutable struct WeaveLaTeX <: WeaveLaTeXFormat
description = "Weave-styled LaTeX"
extension = "tex"
codestart = ""
codeend = ""
termstart = codestart
termend = codeend
outputstart = "\\begin{lstlisting}"
outputend = "\\end{lstlisting}\n"
mimetypes = ["application/pdf", "image/png", "image/jpg", "text/latex", "text/markdown", "text/plain"]
fig_ext = ".pdf"
out_width = "\\linewidth"
out_height = nothing
fig_pos = nothing
fig_env = nothing
# specials
highlight_theme = nothing
template = nothing
keep_unicode = false
tex_deps = ""
# how to escape latex in verbatim/code environment
escape_starter = "(*@"
escape_closer = "@*)"
end
register_format!("md2tex", WeaveLaTeX())
# will be used by `write_doc`
const DEFAULT_LATEX_CMD = ["xelatex", "-shell-escape", "-halt-on-error"]
Base.@kwdef mutable struct LaTeX2PDF <: ExportFormat
primaryformat = WeaveLaTeX()
description = "PDF via LaTeX"
latex_cmd = DEFAULT_LATEX_CMD
end
register_format!("md2pdf", LaTeX2PDF())
register_format!("minted2pdf", LaTeX2PDF(primaryformat=LaTeXMinted()))
function set_format_options!(docformat::LaTeX2PDF; latex_cmd = DEFAULT_LATEX_CMD, _kwargs...)
docformat.latex_cmd = latex_cmd
set_format_options!(docformat.primaryformat; _kwargs...)
end

View File

@ -1,181 +1,180 @@
using Base64
"""
`run(doc::WeaveDoc; doctype = :auto, plotlib="Gadfly",
out_path=:doc, fig_path = "figures", fig_ext = nothing,
cache_path = "cache", cache = :off)`
const PROGRESS_ID = "weave_progress"
Run code chunks and capture output from parsed document.
function run_doc(
doc::WeaveDoc;
doctype::Union{Nothing,AbstractString} = nothing,
out_path::Union{Symbol,AbstractString} = :doc,
args::Any = Dict(),
mod::Union{Module,Nothing} = nothing,
fig_path::Union{Nothing,AbstractString} = nothing,
fig_ext::Union{Nothing,AbstractString} = nothing,
cache_path::AbstractString = "cache",
cache::Symbol = :off,
)
# cache :all, :user, :off, :refresh
* `doctype`: :auto = set based on file extension or specify one of the supported formats.
See `list_out_formats()`
* `plotlib`: `"PyPlot"`, `"Gadfly"`, or `"Plots`
* `out_path`: Path where the output is generated. Can be: `:doc`: Path of the source document, `:pwd`: Julia working directory,
`"somepath"`: Path as a AbstractString e.g `"/home/mpastell/weaveout"`
* `args`: dictionary of arguments to pass to document. Available as WEAVE_ARGS.
* `fig_path`: where figures will be generated, relative to out_path
* `fig_ext`: Extension for saved figures e.g. `".pdf"`, `".png"`. Default setting depends on `doctype`.
* `cache_path`: where of cached output will be saved.
* `cache`: controls caching of code: `:off` = no caching, `:all` = cache everything,
`:user` = cache based on chunk options, `:refresh`, run all code chunks and save new cache.
doc.doctype = isnothing(doctype) ? (doctype = detect_doctype(doc.source)) : doctype
doc.format = deepcopy(get_format(doctype))
**Note:** Run command from terminal and not using IJulia, Juno or ESS, they tend to mess with capturing output.
"""
function Base.run(doc::WeaveDoc; doctype = :auto, plotlib=:auto,
out_path=:doc, args=Dict(), fig_path = "figures", fig_ext = nothing,
cache_path = "cache", cache = :off)
#cache :all, :user, :off, :refresh
cwd = doc.cwd = get_cwd(doc, out_path)
mkpath(cwd)
doc.cwd = get_cwd(doc, out_path)
doctype == :auto && (doctype = detect_doctype(doc.source))
doc.doctype = doctype
doc.format = formats[doctype]
isdir(doc.cwd) || mkpath(doc.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)))
else
DEFAULT_FIG_PATH
end
if contains(doctype, "2pdf") && cache == :off
fig_path = mktempdir(abspath(doc.cwd))
elseif contains(doctype, "2html")
fig_path = mktempdir(abspath(doc.cwd))
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)
cache === :off || @eval import Serialization # XXX: evaluate in a more sensible module
#This is needed for latex and should work on all output formats
is_windows() && (fig_path = replace(fig_path, "\\", "/"))
# New sandbox for each document with args exposed
isnothing(mod) && (mod = sandbox = Core.eval(Main, :(module $(gensym(:WeaveSandBox)) end))::Module)
Core.eval(mod, :(WEAVE_ARGS = $(args)))
doc.fig_path = fig_path
set_rc_params(doc.format.formatdict, fig_path, fig_ext)
mimetypes = doc.format.mimetypes
#New sandbox for each document with args exposed
sandbox = "ReportSandBox$(rcParams[:doc_number])"
eval(parse("module $sandbox\nWEAVE_ARGS=Dict()\nend"))
SandBox = eval(parse(sandbox))
merge!(SandBox.WEAVE_ARGS, args)
rcParams[:doc_number] += 1
report = Report(cwd, doc.basename, doc.format, mimetypes)
cd_back = let d = pwd(); () -> cd(d); end
cd(cwd)
if haskey(doc.format.formatdict, :mimetypes)
mimetypes = doc.format.formatdict[:mimetypes]
else
mimetypes = default_mime_types
end
#Reset plotting
rcParams[:plotlib_set] = false
plotlib == :auto || init_plotting(plotlib)
report = Report(doc.cwd, doc.basename, doc.format.formatdict, mimetypes)
pushdisplay(report)
try
if cache !== :off && cache !== :refresh
cached = read_cache(doc, cache_path)
isnothing(cached) && @info "No cached results found, running code"
if cache != :off && cache != :refresh
cached = read_cache(doc, cache_path)
cached == nothing && info("No cached results found, running code")
else
cached = nothing
end
executed = Any[]
n = length(doc.chunks)
for i = 1:n
chunk = doc.chunks[i]
if isa(chunk, CodeChunk)
options = merge(rcParams[:chunk_defaults], chunk.options)
merge!(chunk.options, options)
end
restore = (cache ==:user && typeof(chunk) == CodeChunk && chunk.options[:cache])
if cached != nothing && (cache == :all || restore)
result_chunks = restore_chunk(chunk, cached)
else
cached = nothing
result_chunks = run_chunk(chunk, report, SandBox)
end
executed = []
n = length(filter(chunk->isa(chunk,CodeChunk), doc.chunks))
i = 0
for chunk in doc.chunks
if chunk isa CodeChunk
options = merge(doc.chunk_defaults, chunk.options)
merge!(chunk.options, options)
executed = [executed; result_chunks]
end
@info "Weaving chunk $(chunk.number) from line $(chunk.start_line)" progress=(i)/n _id=PROGRESS_ID
i+=1
end
doc.header_script = report.header_script
restore = (cache === :user && chunk isa CodeChunk && chunk.options[:cache])
result_chunks = if cached nothing && (cache === :all || restore)
restore_chunk(chunk, cached)
else
run_chunk(chunk, doc, report, mod)
end
executed = [executed; result_chunks]
end
popdisplay(report)
replace_header_inline!(doc, report, mod) # evaluate and replace inline code in header
#Clear variables from used sandbox
clear_sandbox(SandBox)
doc.chunks = executed
doc.header_script = report.header_script
doc.chunks = executed
cache !== :off && write_cache(doc, cache_path)
@isdefined(sandbox) && clear_module!(sandbox)
catch err
rethrow(err)
finally
@info "Weaved all chunks" progress=1 _id=PROGRESS_ID
cd_back()
popdisplay(report) # ensure display pops out even if internal error occurs
# Temporary fig_path is not automatically removed because it contains files so...
!isnothing(fig_path) && startswith(fig_path, "jl_") && rm(normpath(cwd, fig_path), force=true, recursive=true)
if cache != :off
write_cache(doc, cache_path)
end
return doc
end
run_doc(doc::WeaveDoc, doctype::Union{Nothing,AbstractString}; kwargs...) =
run_doc(doc; doctype = doctype, kwargs...)
"""Detect the output format based on file extension"""
function detect_doctype(source::AbstractString)
ext = lowercase(splitext(source)[2])
ext == ".jl" && return "md2html"
contains(ext, "md") && return "md2html"
contains(ext, "rst") && return "rst"
contains(ext, "tex") && return "texminted"
contains(ext, "txt") && return "asciidoc"
"""
detect_doctype(path)
Detect the output format based on file extension.
"""
function detect_doctype(path)
_, ext = lowercase.(splitext(path))
match(r"^\.(jl|.?md|ipynb)", ext) !== nothing && return "md2html"
ext == ".rst" && return "rst"
ext == ".tex" && return "texminted"
ext == ".txt" && return "asciidoc"
return "pandoc"
return "pandoc"
end
function get_cwd(doc, out_path)
return if out_path === :doc
dirname(doc.path)
elseif out_path === :pwd
pwd()
else
path, ext = splitext(out_path)
if isempty(ext) # directory given
path
else # file given
dirname(path)
end
end |> abspath
function run_chunk(chunk::CodeChunk, report::Report, SandBox::Module)
info("Weaving chunk $(chunk.number) from line $(chunk.start_line)")
result_chunks = eval_chunk(chunk, report, SandBox)
contains(report.formatdict[:doctype], "2html") && (result_chunks = embed_figures(result_chunks, report.cwd))
return result_chunks
end
function run_chunk(chunk::CodeChunk, doc, report, mod)
result = eval_chunk(doc, chunk, report, mod)
occursin("2html", doc.doctype) && (embed_figures!(result, report.cwd))
return result
end
function embed_figures!(chunk::CodeChunk, cwd)
for (i, fig) in enumerate(chunk.figures)
chunk.figures[i] = img2base64(fig, 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)
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)
chunk.content = [run_inline(c, doc, report, mod) for c in chunk.content]
function embed_figures(chunk::CodeChunk, cwd)
chunk.figures = [img2base64(fig, cwd) for fig in chunk.figures]
return chunk
end
run_inline(inline::InlineText, ::WeaveDoc, ::Report, ::Module) = inline
function embed_figures(result_chunks, cwd)
for i in 1:length(result_chunks)
figs = result_chunks[i].figures
if !isempty(figs)
result_chunks[i].figures = [img2base64(fig, cwd) for fig in figs]
end
end
return result_chunks
end
const INLINE_OPTIONS = Dict(
:term => false,
:hold => true,
:wrap => false
)
function run_inline(inline::InlineCode, doc::WeaveDoc, report::Report, mod::Module)
# Make a temporary CodeChunk for running code. Collect results and don't wrap
chunk = CodeChunk(inline.content, 0, 0, "", INLINE_OPTIONS)
options = merge(doc.chunk_defaults, chunk.options)
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+xml"), raw)
else
return(fig)
end
end
function run_chunk(chunk::DocChunk, report::Report, SandBox::Module)
chunk.content = [run_inline(c, report, SandBox) for c in chunk.content]
return chunk
end
function run_inline(inline::InlineText, report::Report, SandBox::Module)
return inline
end
function run_inline(inline::InlineCode, report::Report, SandBox::Module)
#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(rcParams[:chunk_defaults], chunk.options)
merge!(chunk.options, options)
chunks = eval_chunk(doc, chunk, report, mod)
occursin("2html", doc.doctype) && (embed_figures!(chunks, report.cwd))
chunks = eval_chunk(chunk, report, SandBox)
contains(report.formatdict[:doctype], "2html") && (chunks = embed_figures(chunks, report.cwd))
output = chunks[1].output
endswith(output, "\n") && (output = output[1:end-1])
@ -185,267 +184,318 @@ function run_inline(inline::InlineCode, doc::WeaveDoc, report::Report, mod::Modu
return inline
end
function run_code(doc::WeaveDoc, chunk::CodeChunk, report::Report, mod::Module)
code = chunk.content
path = doc.path
error = chunk.options[:error]
codes = chunk.options[:term] ? split_code(code) : [code]
capture = code -> capture_output(code, mod, path, error, report)
return capture.(codes)
function reset_report(report::Report)
report.cur_result = ""
report.figures = AbstractString[]
report.term_state = :text
end
function split_code(code)
res = String[]
e = 1
ex = :init
while true
s = e
ex, e = Meta.parse(code, s)
isnothing(ex) && break
push!(res, strip(code[s:e-1]))
function run_code(chunk::CodeChunk, report::Report, SandBox::Module)
expressions = parse_input(chunk.content)
N = length(expressions)
#@show expressions
result_no = 1
results = ChunkOutput[ ]
for (str_expr, expr) = expressions
reset_report(report)
lastline = (result_no == N)
rcParams[:plotlib_set] || detect_plotlib(chunk) #Try to autodetect plotting library
(obj, out) = capture_output(expr, SandBox, chunk.options[:term],
chunk.options[:display], rcParams[:plotlib], lastline)
figures = report.figures #Captured figures
result = ChunkOutput(str_expr, out, report.cur_result, report.rich_output, figures)
report.rich_output = ""
push!(results, result)
result_no += 1
end
return res
end
function capture_output(code, mod, path, error, report)
reset_report!(report)
old = stdout
rw, wr = redirect_stdout()
reader = @async read(rw, String)
local out = nothing
task_local_storage(:SOURCE_PATH, path) do
try
obj = include_string(mod, code, path) # TODO: fix line number
!isnothing(obj) && !REPL.ends_with_semicolon(code) && display(obj)
catch _err
err = unwrap_load_err(_err)
error || throw(err)
display(err)
@warn "ERROR: $(typeof(err)) occurred, including output in Weaved document"
finally
redirect_stdout(old)
close(wr)
out = fetch(reader)
close(rw)
#Save figures only in the end of chunk for PyPlot
if rcParams[:plotlib] == "PyPlot"
#Fix "world-age" issue
if VERSION >= v"0.6-alpha"
savep(x) = eval(Expr(:call, x-> savefigs_pyplot(x), x))
savep(report)
else
savefigs_pyplot(report)
end
end
return ChunkOutput(code, remove_ansi_control_chars(out), report.rich_output, report.figures)
return results
end
function reset_report!(report)
report.rich_output = ""
report.figures = String[]
getstdout() = Base.STDOUT
function capture_output(expr, SandBox::Module, term, disp, plotlib,
lastline)
#oldSTDOUT = STDOUT
oldSTDOUT = getstdout()
out = nothing
obj = nothing
rw, wr = redirect_stdout()
reader = @async readstring(rw)
try
obj = eval(SandBox, expr)
if (term || disp) && typeof(expr) == Expr && expr.head != :toplevel
obj != nothing && display(obj)
elseif typeof(expr) == Symbol
display(obj)
elseif plotlib == "Gadfly" && typeof(obj) == Gadfly.Plot
obj != nothing && display(obj)
#This shows images and lone variables, result can
#Handle last line sepately
elseif lastline && obj != nothing
#elseif mimewritable("image/png", obj) && expr.head == :call
if expr.head == :call
display(obj)
end
end
finally
redirect_stdout(oldSTDOUT)
close(wr)
out = wait(reader)
close(rw)
end
return (obj, out)
end
unwrap_load_err(err) = return err
unwrap_load_err(err::LoadError) = return err.error
# https://stackoverflow.com/a/33925425/12113178
remove_ansi_control_chars(s) = replace(s, r"(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]" => "")
#Parse chunk input to array of expressions
function parse_input(input::AbstractString)
parsed = Tuple{AbstractString, Any}[]
input = lstrip(input)
n = length(input)
pos = 1 #The first character is extra line end
while pos n
oldpos = pos
code, pos = parse(input, pos)
push!(parsed, (input[oldpos:pos-1] , code ))
end
parsed
end
function eval_chunk(chunk::CodeChunk, report::Report, SandBox::Module)
function eval_chunk(doc::WeaveDoc, chunk::CodeChunk, report::Report, mod::Module)
if !chunk.options[:eval]
chunk.output = ""
chunk.options[:fig] = false
return chunk
end
execute_prehooks!(chunk)
#Run preexecute_hooks
for hook in preexecute_hooks
chunk = hook(chunk)
end
report.fignum = 1
report.cur_chunk = chunk
if hasproperty(report.format, :out_width) && isnothing(chunk.options[:out_width])
chunk.options[:out_width] = report.format.out_width
if haskey(report.formatdict, :out_width) && chunk.options[:out_width] == nothing
chunk.options[:out_width] = report.formatdict[:out_width]
end
chunk.result = run_code(doc, chunk, report, mod)
chunk.result = run_code(chunk, report, SandBox)
execute_posthooks!(chunk)
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,
const preexecution_hooks = Function[]
push_preexecution_hook!(f::Function) = push!(preexecution_hooks, f)
function pop_preexecution_hook!(f::Function)
i = findfirst(x -> x == f, preexecution_hooks)
isnothing(i) && error("this function has not been registered in the pre-execution hook yet")
return splice!(preexecution_hooks, i)
end
function execute_prehooks!(chunk::CodeChunk)
for prehook in preexecution_hooks
Base.invokelatest(prehook, chunk)
#Run post_execute chunks
for hook in postexecute_hooks
chunk = hook(chunk)
end
end
const postexecution_hooks = Function[]
push_postexecution_hook!(f::Function) = push!(postexecution_hooks, f)
function pop_postexecution_hook!(f::Function)
i = findfirst(x -> x == f, postexecution_hooks)
isnothing(i) && error("this function has not been registered in the post-execution hook yet")
return splice!(postexecution_hooks, i)
end
function execute_posthooks!(chunk::CodeChunk)
for posthook in postexecution_hooks
Base.invokelatest(posthook, chunk)
if chunk.options[:term]
chunks = collect_results(chunk, TermResult())
elseif chunk.options[:hold]
chunks = collect_results(chunk, CollectResult())
else
chunks = collect_results(chunk, ScriptResult())
end
#else
# chunk.options[:fig] && (chunk.figures = copy(report.figures))
#end
chunks
end
"""
clear_module!(mod::Module)
Recursively sets variables in `mod` to `nothing` so that they're GCed.
#function eval_chunk(chunk::DocChunk, report::Report, SandBox)
# chunk
#end
!!! warning
`const` variables can't be reassigned, as such they can't be cleared.
"""
function clear_module!(mod::Module)
for name in names(mod; all = true)
name === :eval && continue
try
v = getfield(mod, name)
if v isa Module && v != mod
clear_module!(v)
continue
end
isconst(mod, name) && continue # can't clear constant
Core.eval(mod, :($name = nothing))
catch err
@debug err
#Set all variables to nothing
function clear_sandbox(SandBox::Module)
for name = names(SandBox, true)
if name != :eval && name != names(SandBox)[1]
try eval(SandBox, parse(AbstractString(AbstractString(name), "=nothing"))) end
end
end
end
function get_figname(report::Report, chunk; fignum = nothing, ext = nothing)
isnothing(ext) && (ext = chunk.options[:fig_ext])
isnothing(fignum) && (fignum = report.fignum)
chunkid = isnothing(chunk.options[:label]) ? chunk.number : chunk.options[:label]
basename = string(report.basename, '_', chunkid, '_', fignum, ext)
full_name = normpath(report.cwd, chunk.options[:fig_path], basename)
rel_name = string(chunk.options[:fig_path], '/', basename) # Relative path is used in output
function get_figname(report::Report, chunk; fignum = nothing, ext = nothing)
figpath = joinpath(report.cwd, chunk.options[:fig_path])
isdir(figpath) || mkpath(figpath)
ext == nothing && (ext = chunk.options[:fig_ext])
fignum == nothing && (fignum = report.fignum)
chunkid = (chunk.options[:name] == nothing) ? chunk.number : chunk.options[:name]
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 set_rc_params(doc::WeaveDoc, fig_path, fig_ext)
doc.chunk_defaults[:fig_ext] = isnothing(fig_ext) ? doc.format.fig_ext : fig_ext
doc.chunk_defaults[:fig_path] = fig_path
function init_plotting(plotlib)
srcdir = escape_string(dirname(@__FILE__))
rcParams[:plotlib_set] = true
if plotlib == nothing
rcParams[:plotlib] = nothing
else
l_plotlib = lowercase(plotlib)
rcParams[:chunk_defaults][:fig] = true
if l_plotlib == "pyplot"
eval(parse("""include("$srcdir/pyplot.jl")"""))
rcParams[:plotlib] = "PyPlot"
elseif l_plotlib == "plots"
eval(parse("""include("$srcdir/plots.jl")"""))
rcParams[:plotlib] = "Plots"
elseif l_plotlib == "gadfly"
eval(parse("""include("$srcdir/gadfly.jl")"""))
rcParams[:plotlib] = "Gadfly"
end
end
return true
end
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 any(!isempty strip, (r.stdout, r.rich_output)) || !isempty(r.figures)
rchunk = CodeChunk(
content,
chunk.number,
chunk.start_line,
chunk.optionstring,
copy(chunk.options),
)
rchunk.output = r.stdout
rchunk.rich_output = r.rich_output
rchunk.figures = r.figures
push!(result_chunks, rchunk)
content = ""
function get_cwd(doc::WeaveDoc, out_path)
#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
splitted = splitext(out_path)
if splitted[2] == ""
cwd = expanduser(out_path)
else
cwd = splitdir(expanduser(out_path))[1]
end
end
if !isempty(content)
rchunk = CodeChunk(
content,
chunk.number,
chunk.start_line,
chunk.optionstring,
copy(chunk.options),
)
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])
splitted = splitext(out_path)
if (splitted[2]) == ""
outname = "$(doc.cwd)/$(doc.basename).$ext"
else
outname = expanduser(out_path)
end
end
function set_rc_params(formatdict, fig_path, fig_ext)
if fig_ext == nothing
rcParams[:chunk_defaults][:fig_ext] = formatdict[:fig_ext]
docParams[:fig_ext] = formatdict[:fig_ext]
else
rcParams[:chunk_defaults][:fig_ext] = fig_ext
docParams[:fig_ext] = fig_ext
end
rcParams[:chunk_defaults][:fig_path] = fig_path
docParams[: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) == ""
content *= r.code
else
content = "\n" * content * r.code
rchunk = CodeChunk(content, chunk.number, chunk.start_line, chunk.optionstring, copy(chunk.options))
content = ""
rchunk.result_no = result_no
result_no *=1
rchunk.figures = r.figures
rchunk.output = r.stdout * r.displayed
rchunk.rich_output = r.rich_output
push!(result_chunks, rchunk)
end
end
if content != ""
startswith(content, "\n") || (content = "\n" * content)
rchunk = CodeChunk(content, chunk.number, chunk.start_line, chunk.optionstring, copy(chunk.options))
push!(result_chunks, rchunk)
end
return result_chunks
end
function collect_term_results(chunk::CodeChunk)
function collect_results(chunk::CodeChunk, fmt::TermResult)
output = ""
prompt = chunk.options[:prompt]
result_chunks = CodeChunk[]
for r in chunk.result
output *= string('\n', indent_term_code(prompt, r.code), '\n', r.stdout)
result_no = 1
result_chunks = CodeChunk[ ]
for r = 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
push!(result_chunks, rchunk)
end
end
if !isempty(output)
rchunk = CodeChunk(
"",
chunk.number,
chunk.start_line,
chunk.optionstring,
copy(chunk.options),
)
rchunk.output = output
if output != ""
rchunk = CodeChunk("", chunk.number, chunk.start_line, chunk.optionstring, copy(chunk.options))
rchunk.output = output
push!(result_chunks, rchunk)
end
return result_chunks
end
function indent_term_code(prompt, code)
prompt_with_space = string(prompt, ' ')
n = length(prompt_with_space)
pads = ' ' ^ n
return map(enumerate(split(code, '\n'))) do (i,line)
isone(i) ? string(prompt_with_space, line) : string(pads, line)
end |> joinlines
end
function collect_hold_results(chunk::CodeChunk)
for r in chunk.result
chunk.output *= r.stdout
function collect_results(chunk::CodeChunk, fmt::CollectResult)
result_no = 1
for r =chunk.result
chunk.output *= r.stdout
chunk.rich_output *= r.rich_output
chunk.figures = [chunk.figures; r.figures]
end
return [chunk]
end
const HEADER_INLINE = Regex("$(HEADER_INLINE_START)(?<code>.+)$(HEADER_INLINE_END)")
function detect_plotlib(chunk::CodeChunk)
replace_header_inline!(doc, report, mod) = _replace_header_inline!(doc, doc.header, report, mod)
if isdefined(:Plots)
init_plotting("Plots")
#Need to set size before plots are created
plots_set_size(chunk)
return
end
function _replace_header_inline!(doc, header, report, mod)
replace!(header) do (k,v)
return k =>
v isa Dict ? _replace_header_inline!(doc, v, report, mod) :
!isa(v, AbstractString) ? v :
replace(v, HEADER_INLINE => s -> begin
code = replace(s, HEADER_INLINE => s"\g<code>")
run_inline_code(code, doc, report, mod)
end)
end
return header
end
function run_inline_code(code, doc, report, mod)
inline = InlineCode(code, 1, :inline)
inline = run_inline(inline, doc, report, mod)
return strip(inline.output, '"')
isdefined(:PyPlot) && init_plotting("PyPlot") && return
isdefined(:Gadfly) && init_plotting("Gadfly") && return
end

View File

@ -1,72 +0,0 @@
# TODO: concreate typing
abstract type WeaveChunk end
abstract type Inline end
abstract type WeaveFormat end
mutable struct WeaveDoc
source::AbstractString
basename::AbstractString
path::AbstractString
chunks::Vector{WeaveChunk}
cwd::AbstractString
format::Any
doctype::String
header_script::String
header::Dict
chunk_defaults::Dict{Symbol,Any}
end
struct ChunkOutput
code::String
stdout::String
rich_output::String
figures::Vector{String}
end
mutable struct CodeChunk <: WeaveChunk
content::String
number::Int
start_line::Int
optionstring::String
options::Dict{Symbol,Any}
output::AbstractString
rich_output::AbstractString
figures::Vector{String}
result::Vector{ChunkOutput}
end
function CodeChunk(content, number, start_line, optionstring, options)
return CodeChunk(
string(rstrip(content), '\n'), # normalize end of chunk)
number,
start_line,
optionstring,
options,
"",
"",
AbstractString[],
ChunkOutput[]
)
end
mutable struct DocChunk <: WeaveChunk
content::Vector{Inline}
number::Int
start_line::Int
end
struct InlineText <: Inline
content::String
number::Int
end
mutable struct InlineCode <: Inline
content::String
number::Int
ctype::Symbol
output::String
rich_output::String
figures::Vector{String}
end
InlineCode(content, number, ctype) = InlineCode(content, number, ctype, "", "", String[])

View File

@ -1,19 +0,0 @@
function write_doc(docformat::LaTeX2PDF, doc, rendered, out_path)
cd_back = let d = pwd(); () -> cd(d); end
cd(doc.cwd)
try
tex_path = basename(out_path)
write(tex_path, rendered)
cmds = copy(docformat.latex_cmd)
push!(cmds, tex_path)
cmd = Cmd(cmds)
run(cmd); run(cmd) # XXX: is twice enough for every case ?
catch
@warn "Error converting document to pdf. Try running latex manually"
rethrow()
finally
cd_back()
end
return get_out_path(doc, out_path, "pdf")
end

View File

@ -1,76 +0,0 @@
function write_doc(docformat::Pandoc2HTML, doc, rendered, out_path)
_, weave_source = splitdir(abspath(doc.source))
weave_version, weave_date = weave_info()
# 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
out_path = get_out_path(doc, out_path, "html")
cd_back = let d = pwd(); () -> cd(d); end
cd(dirname(out_path))
try
out = basename(out_path)
highlight_stylesheet = get_highlight_stylesheet(MIME("text/html"), docformat.highlight_theme)
cmd = `pandoc -f markdown+raw_html -s --mathjax=""
$filt $citeproc $(docformat.pandoc_options)
--template $(docformat.template_path)
-H $(docformat.stylesheet_path)
$(self_contained)
-V highlight_stylesheet=$(highlight_stylesheet)
-V weave_version=$(weave_version)
-V weave_date=$(weave_date)
-V weave_source=$(weave_source)
-V headerscript=$(header_script)
-o $(out)`
proc = open(cmd, "r+")
println(proc.in, rendered)
close(proc.in)
proc_output = read(proc.out, String)
catch
rethrow() # TODO: just show error content instead of rethrow the err
finally
cd_back()
end
return out_path
end
function write_doc(docformat::Pandoc2PDF, doc, rendered, out_path)
if haskey(doc.header, "bibliography")
filt = "--filter"
citeproc = "pandoc-citeproc"
else
filt = []
citeproc = []
end
out_path = get_out_path(doc, out_path, "pdf")
cd_back = let d = pwd(); () -> cd(d); end
cd(dirname(out_path))
try
out = basename(out_path)
cmd = `pandoc -f markdown+raw_tex -s --pdf-engine=xelatex --highlight-style=tango
$filt $citeproc $(docformat.pandoc_options)
--include-in-header=$(docformat.header_template)
-o $(out)`
proc = open(cmd, "r+")
println(proc.in, rendered)
close(proc.in)
proc_output = read(proc.out, String)
catch
rethrow()
finally
cd_back()
end
return out_path
end

View File

@ -1,11 +0,0 @@
function write_doc(doc, rendered, out_path)
return write_doc(doc.format, doc, rendered, out_path)
end
function write_doc(::WeaveFormat, doc, rendered, out_path)
write(out_path, rendered)
return out_path
end
include("pandoc.jl")
include("latex.jl")

167
src/writers.jl Normal file
View File

@ -0,0 +1,167 @@
import JSON
type NotebookOutput
end
type MarkdownOutput
end
type NowebOutput
end
type ScriptOutput
end
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 == ".jl" && return "script"
ext == ".jmd" && return "markdown"
ext == ".ipynb" && return "notebook"
return "noweb"
end
"""
`convert_doc(infile::AbstractString, outfile::AbstractString; format = nothing)`
Convert Weave documents between different formats
* `infile` = Name of the input document
* `outfile` = Name of the output document
* `format` = Output format (optional). Detected from outfile extension, but can
be set to `"script"`, `"markdown"`, `"notebook"` or `"noweb"`.
"""
function convert_doc(infile::AbstractString, outfile::AbstractString; format = nothing)
doc = read_doc(infile)
if format == nothing
format = detect_outformat(outfile)
end
converted = convert_doc(doc, output_formats[format])
open(outfile, "w") do f
write(f, converted)
end
return nothing
end
"""Convert Weave document to Jupyter notebook format"""
function convert_doc(doc::WeaveDoc, format::NotebookOutput)
nb = Dict()
nb["nbformat"] = 4
nb["nbformat_minor"] = 2
metadata = Dict()
kernelspec = Dict()
kernelspec["language"] = "julia"
kernelspec["name"] = "julia-0.5"
kernelspec["display_name"] = "Julia 0.5.0"
metadata["kernelspec"] = kernelspec
language_info = Dict()
language_info["file_extension"] = ".jl"
language_info["mimetype"] = "application/julia"
language_info["name"]= "julia"
language_info["version"] = "0.5.0"
metadata["language_info"] = language_info
cells = []
ex_count = 1
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], ""))])
)
else
push!(cells,
Dict("cell_type" => "code",
"metadata" => Dict(),
"source" => [strip(chunk.content)],
"execution_count" => nothing,
"outputs" => []
))
end
end
nb["cells"] = cells
nb["metadata"] = metadata
json_nb = JSON.json(nb, 2)
return json_nb
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"
end
end
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"
end
end
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"
end
end
return output
end
function Base.repr(c::InlineText)
return c.content
end
function Base.repr(c::InlineCode)
return "`j $(c.content)`"
end

50
templates/julia_html.tpl Normal file
View File

@ -0,0 +1,50 @@
<!DOCTYPE html>
<HTML lang = "en">
<HEAD>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
{{#:title}}<title>{{:title}}</title>{{/:title}}
{{{ :header_script }}}
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
tex2jax: {inlineMath: [['$','$'], ['\\(','\\)']]}
});
</script>
<script src='https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML'></script>
<style type="text/css">
{{{ :themecss }}}
</style>
{{{ :highlightcss }}}
</HEAD>
<BODY>
<div class ="container">
<div class = "row">
<div class = "col-md-12 twelve columns">
<div class="title">
{{#:title}}<h1 class="title">{{:title}}</h1>{{/:title}}
{{#:author}}<h5>{{{:author}}}</h5>{{/:author}}
{{#:date}}<h5>{{{:date}}}</h5>{{/:date}}
</div>
{{{ :body }}}
<HR/>
<div class="footer"><p>
Published from <a href="{{{:source}}}">{{{:source}}}</a> using
<a href="http://github.com/mpastell/Weave.jl">Weave.jl</a>
{{:wversion}} on {{:wtime}}.
<p></div>
</div>
</div>
</div>
</BODY>
</HTML>

View File

@ -3,13 +3,13 @@
\usepackage[a4paper,text={16.5cm,25.2cm},centering]{geometry}
\usepackage{lmodern}
\usepackage{amssymb,amsmath}
\usepackage{bm}
\usepackage{graphicx}
\usepackage{ifxetex}
\ifxetex
\usepackage{mathspec}
\fi
\usepackage{graphics}
\usepackage{microtype}
\usepackage{hyperref}
{{#:tex_deps}}
{{{ :tex_deps }}}
{{/:tex_deps}}
\setlength{\parindent}{0pt}
\setlength{\parskip}{1.2ex}
@ -22,6 +22,7 @@
urlcolor=blue
}
{{#:title}}
\title{ {{{ :title }}} }
{{/:title}}
@ -34,7 +35,7 @@
\date{ {{{ :date }}} }
{{/:date}}
{{ :highlight }}
{{{ :highlight }}}
\begin{document}

View File

@ -1,50 +0,0 @@
<!DOCTYPE html>
<HTML lang = "en">
<HEAD>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
{{#:title}}<title>{{:title}}</title>{{/:title}}
{{{ :header_script }}}
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
tex2jax: {inlineMath: [['$','$'], ['\\(','\\)']]},
TeX: { equationNumbers: { autoNumber: "AMS" } }
});
</script>
<script type="text/javascript" async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
</script>
{{{ :highlight_stylesheet }}}
<style type="text/css">
{{{ :stylesheet }}}
</style>
</HEAD>
<BODY>
<div class ="container">
<div class = "row">
<div class = "col-md-12 twelve columns">
<div class="title">
{{#:title}}<h1 class="title">{{:title}}</h1>{{/:title}}
{{#:author}}<h5>{{{:author}}}</h5>{{/:author}}
{{#:date}}<h5>{{{:date}}}</h5>{{/:date}}
</div>
{{{ :body }}}
<HR/>
<div class="footer">
<p>
Published from <a href="{{{:weave_source}}}">{{{:weave_source}}}</a>
using <a href="http://github.com/JunoLab/Weave.jl">Weave.jl</a> {{:weave_version}} on {{:weave_date}}.
</p>
</div>
</div>
</div>
</div>
</BODY>
</HTML>

View File

@ -1,13 +1,16 @@
<style type="text/css">
@font-face {
font-family: 'Garamond';
font-style: normal;
font-weight: 300;
}
@font-face {
font-family: 'Garamond';
font-style: normal;
font-weight: 400;
}
@font-face {
font-family: 'Garamond';
font-style: normal;
font-weight: 600;
}
@ -181,8 +184,6 @@ optgroup {
font-weight: bold;
}
table {
font-family: monospace, monospace;
font-size : 0.8em;
border-collapse: collapse;
border-spacing: 0;
}
@ -190,13 +191,7 @@ td,
th {
padding: 0;
}
thead th {
border-bottom: 1px solid black;
background-color: white;
}
tr:nth-child(odd){
background-color: rgb(248,248,248);
}
/*
* Skeleton V2.0.4
@ -508,7 +503,7 @@ pre.sourceCode.julia {
display: block;
padding: 9.5px;
margin: 0 0 10px;
font-size: 12px;
font-size: 13px;
line-height: 1.42857143;
color: #333;
word-break: break-all;
@ -518,10 +513,6 @@ pre.sourceCode.julia {
border-radius: 4px;
}
pre.julia-error {
color : red
}
code,
kbd,
pre,
@ -529,11 +520,16 @@ samp {
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
}
code {
padding: 2px 4px;
font-size: 90%;
background-color: #ffffff;
border-radius: 4px;
}
code.sourceCode.julia {
padding: 2px 4px;
font-size: 90%;
background-color: #f5f5f5;
border-radius: 4px;
}
@ -546,5 +542,5 @@ code.sourceCode.julia {
@media (min-width: 1200px) {}
h1.title {margin-top : 20px}
img {max-width : 100%}
img {max-width : 100%%}
</style>

View File

@ -32,8 +32,6 @@ $for(header-includes)$
$header-includes$
$endfor$
$highlight_stylesheet$
$if(highlighting-css)$
<style type="text/css">
$highlighting-css$
@ -85,8 +83,8 @@ $endfor$
<HR/>
<div class="footer"><p>
Published from <a href="$source$">$source$</a> using
<a href="http://github.com/mpastell/Weave.jl">Weave.jl</a> $weave_version$ on $weave_date$.
Published from <a href="$wsource$">$wsource$</a> using
<a href="http://github.com/mpastell/Weave.jl">Weave.jl</a> $wversion$ on $wtime$.
<p></div>
</div>

View File

@ -180,8 +180,6 @@ optgroup {
font-weight: bold;
}
table {
font-family: monospace, monospace;
font-size : 0.8em;
border-collapse: collapse;
border-spacing: 0;
}
@ -189,13 +187,6 @@ td,
th {
padding: 0;
}
thead th {
border-bottom: 1px solid black;
background-color: white;
}
tr:nth-child(odd){
background-color: rgb(248,248,248);
}
/*
@ -434,7 +425,8 @@ ol ol,
ol ul {
margin: 1.5rem 0 1.5rem 3rem;
font-size: 90%; }
li > p {margin : 0;}
li {
margin-bottom: 1rem; }
th,
td {
padding: 12px 15px;
@ -463,7 +455,7 @@ p,
ul,
ol,
form {
margin-bottom: 1.0rem; }
margin-bottom: 2.5rem; }
.u-full-width {
width: 100%;
box-sizing: border-box; }
@ -492,30 +484,26 @@ pre {
margin: 0 0 10px;
font-size: 13px;
line-height: 1.42857143;
color: #333;
word-break: break-all;
word-wrap: break-word;
background-color: #ffffff;
border: 1px solid #ccc;
border-radius: 4px;
}
pre.hljl {
margin: 0 0 10px;
pre.sourceCode.julia {
display: block;
background: #f5f5f5;
padding: 9.5px;
margin: 0 0 10px;
font-size: 13px;
line-height: 1.42857143;
color: #333;
word-break: break-all;
word-wrap: break-word;
background-color: #f5f5f5;
border: 1px solid #ccc;
border-radius: 4px;
padding : 5px;
}
pre.output {
background: #ffffff;
}
pre.code {
background: #ffffff;
}
pre.julia-error {
color : red
}
code,
@ -523,9 +511,14 @@ kbd,
pre,
samp {
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
font-size: 0.9em;
}
code {
padding: 2px 4px;
font-size: 90%;
background-color: #ffffff;
border-radius: 4px;
}
@media (min-width: 400px) {}
@media (min-width: 550px) {}
@ -534,5 +527,5 @@ samp {
@media (min-width: 1200px) {}
h1.title {margin-top : 20px}
img {max-width : 100%}
img {max-width : 100%%}
div.title {text-align: center;}

7
test/REQUIRE Normal file
View File

@ -0,0 +1,7 @@
julia 0.5
Cairo
Gadfly
PyPlot
GR
DSP
Plots

View File

@ -1,28 +1,45 @@
# Test if running document with and without cache works
using Weave
using Base.Test
#Test if running document with and without cache works
isdir("documents/cache") && rm("documents/cache", recursive = true)
weave("documents/chunk_options.noweb", cache=:all)
result = read("documents/chunk_options.md", String)
weave("documents/chunk_options.noweb", plotlib=nothing, cache=:all)
result = readstring("documents/chunk_options.md")
rm("documents/chunk_options.md")
weave("documents/chunk_options.noweb", cache=:all)
cached_result = read("documents/chunk_options.md", String)
weave("documents/chunk_options.noweb", plotlib=nothing, cache=:all)
cached_result = readstring("documents/chunk_options.md")
@test result == cached_result
# cache = :user
isdir("documents/cache") && rm("documents/cache", recursive = true)
out = "documents/chunk_cache.md"
Weave.weave("documents/chunk_cache.noweb", cache=:user);
result = read(out, String)
Weave.weave("documents/chunk_cache.noweb", plotlib=nothing, cache=:user);
result = readstring(out)
rm(out)
Weave.weave("documents/chunk_cache.noweb", cache=:user);
cached_result = read(out, String)
Weave.weave("documents/chunk_cache.noweb", plotlib=nothing, cache=:user);
cached_result = readstring(out)
@test result == cached_result
# cache = :all
isdir("documents/cache") && rm("documents/cache", recursive = true)
out = "documents/chunk_cache.md"
Weave.weave("documents/chunk_cache.noweb", cache=:all);
result = read(out, String)
result = readstring(out)
rm(out)
Weave.weave("documents/chunk_cache.noweb", cache=:all);
cached_result = read(out, String)
cached_result = readstring(out)
@test result == cached_result
if VERSION.minor == 5
using Gadfly
isdir("documents/cache") && rm("documents/cache", recursive = true)
#Caching with Gadfly
weave("documents/gadfly_formats_test.txt", doctype="tex", plotlib="gadfly", cache=:all)
result = readstring("documents/gadfly_formats_test.tex")
rm("documents/gadfly_formats_test.tex")
weave("documents/gadfly_formats_test.txt", doctype="tex", plotlib="gadfly", cache=:all)
cached_result = readstring("documents/gadfly_formats_test.tex")
@test result == cached_result
end

19
test/chunk_options.jl Normal file
View File

@ -0,0 +1,19 @@
using Weave
using Base.Test
cleanup = true
VER = "$(VERSION.major).$(VERSION.minor)"
weave("documents/chunk_options.noweb", plotlib=nothing)
result = readstring("documents/chunk_options.md")
ref = readstring("documents/$VER/chunk_options_ref.md")
@test result == ref
cleanup && rm("documents/chunk_options.md")
tangle("documents/chunk_options.noweb", out_path = "documents/tangle")
result = readstring("documents/tangle/chunk_options.jl")
ref = readstring("documents/tangle/chunk_options.jl.ref")
@test ref == result
cleanup && rm("documents/tangle/chunk_options.jl")

18
test/chunk_opts_gadfly.jl Normal file
View File

@ -0,0 +1,18 @@
using Weave
using Base.Test
cleanup = true
#Test hold and term options
weave("documents/test_hold.mdw", doctype="pandoc", plotlib="Gadfly")
result = readstring("documents/test_hold.md")
ref = readstring("documents/test_hold_ref.md")
@test result == ref
cleanup && rm("documents/test_hold.md")
#Test setting and restoring chunk options
Weave.weave("documents/default_opts.noweb", doctype = "tex")
result = readstring("documents/default_opts.tex")
ref = readstring("documents/default_opts_ref.tex")
@test result == ref
cleanup && rm("documents/default_opts.tex")

29
test/convert_test.jl Normal file
View File

@ -0,0 +1,29 @@
using Weave
using Base.Test
function convert_test(outfile)
outfile = joinpath("documents/convert", outfile)
infile = "documents/chunk_options.noweb"
convert_doc(infile, outfile)
result = readstring(outfile)
ref = readstring(outfile * ".ref")
@test result == ref
rm(outfile)
end
convert_test("chunk_options.jmd")
convert_test("chunk_options.jl")
convert_test("chunk_options.mdw")
convert_test("chunk_options.ipynb")
function convert_test_nb(outfile)
outfile = joinpath("documents/convert", outfile)
infile = "documents/chunk_options.ipynb"
convert_doc(infile, outfile)
result = readstring(outfile)
ref = readstring(outfile * ".ref")
rm(outfile)
@test result == ref
end
convert_test_nb("chunk_options_nb.mdw")

View File

@ -0,0 +1,145 @@
~~~~{.julia}
julia> y= [2, 5, 12]
3-element Array{Int64,1}:
2
5
12
~~~~~~~~~~~~~
~~~~{.julia}
julia> x = 1:10
1:10
julia> d = Dict("Weave" => "testing")
Dict{String,String} with 1 entry:
"Weave" => "testing"
julia> y = [2, 4 ,8]
3-element Array{Int64,1}:
2
4
8
~~~~~~~~~~~~~
~~~~{.julia}
x = [12, 10]
println(y)
~~~~~~~~~~~~~
~~~~
[2,4,8]
~~~~
~~~~{.julia}
println(x)
~~~~~~~~~~~~~
~~~~
[12,10]
~~~~
~~~~
Results without code
~~~~
~~~~
[12,10]
~~~~
~~~~{.julia}
y = randn(5)
println("Don't eval, but show code")
~~~~~~~~~~~~~
~~~~{.julia}
y = 1:5
println(y)
~~~~~~~~~~~~~
~~~~
1:5
~~~~
~~~~{.julia}
a = "Don't print me"
println(a)
~~~~~~~~~~~~~
~~~~{.julia}
println("No markup for results.")
~~~~~~~~~~~~~
No markup for results.
Test wrapping:
~~~~{.julia}
println(collect(0:10:1000))
~~~~~~~~~~~~~
~~~~
[0,10,20,30,40,50,60,70,80,90,100,110,120,130,140,150,160,170,180,190,200,2
10,220,230,240,250,260,270,280,290,300,310,320,330,340,350,360,370,380,390,
400,410,420,430,440,450,460,470,480,490,500,510,520,530,540,550,560,570,580
,590,600,610,620,630,640,650,660,670,680,690,700,710,720,730,740,750,760,77
0,780,790,800,810,820,830,840,850,860,870,880,890,900,910,920,930,940,950,9
60,970,980,990,1000]
~~~~
~~~~{.julia}
println(collect(0:10:1000))
~~~~~~~~~~~~~
~~~~
[0,10,20,30,40,50,60,70,80,90,100,110,120,130,140,150,160,170,180,190,200,210,220,230,240,250,260,270,280,290,300,310,320,330,340,350,360,370,380,390,400,410,420,430,440,450,460,470,480,490,500,510,520,530,540,550,560,570,580,590,600,610,620,630,640,650,660,670,680,690,700,710,720,730,740,750,760,770,780,790,800,810,820,830,840,850,860,870,880,890,900,910,920,930,940,950,960,970,980,990,1000]
~~~~
~~~~{.julia}
println(collect(0:10:1000))
~~~~~~~~~~~~~
~~~~
[0,10,20,30,40,50,60,70,80,90,100,110,120,130,140,150,160,17
0,180,190,200,210,220,230,240,250,260,270,280,290,300,310,32
0,330,340,350,360,370,380,390,400,410,420,430,440,450,460,47
0,480,490,500,510,520,530,540,550,560,570,580,590,600,610,62
0,630,640,650,660,670,680,690,700,710,720,730,740,750,760,77
0,780,790,800,810,820,830,840,850,860,870,880,890,900,910,92
0,930,940,950,960,970,980,990,1000]
~~~~

View File

@ -0,0 +1,80 @@
#' % FIR filter design with Julia
#' % Matti Pastell
#' % 21th April 2016
#' # Introduction
#' This an example of a julia script that can be published using
#' [Weave](http://mpastell.github.io/Weave.jl/latest/usage/).
#' The script can be executed normally using Julia
#' or published to HTML or pdf with Weave.
#' Text is written in markdown in lines starting with "`#'` " and code
#' is executed and results are included in the published document.
#' Notice that you don't need to define chunk options, but you can using
#' `#+`. just before code e.g. `#+ term=True, caption='Fancy plots.'`.
#' If you're viewing the published version have a look at the
#' [source](FIR_design.jl) to see the markup.
#' # FIR Filter Design
#' We'll implement lowpass, highpass and ' bandpass FIR filters. If
#' you want to read more about DSP I highly recommend [The Scientist
#' and Engineer's Guide to Digital Signal
#' Processing](http://www.dspguide.com/) which is freely available
#' online.
#' ## Calculating frequency response
#' DSP.jl package doesn't (yet) have a method to calculate the
#' the frequency response of a FIR filter so we define it:
using Gadfly, DSP
function FIRfreqz(b::Array, w = linspace(0, π, 1024))
n = length(w)
h = Array{Complex64}(n)
sw = 0
for i = 1:n
for j = 1:length(b)
sw += b[j]*exp(-im*w[i])^-j
end
h[i] = sw
sw = 0
end
return h
end
#' ## Design Lowpass FIR filter
#' Designing a lowpass FIR filter is very simple to do with DSP.jl, all you
#' need to do is to define the window length, cut off frequency and the
#' window. We will define a lowpass filter with cut off frequency at 5Hz for a signal
#' sampled at 20 Hz.
#' We will use the Hamming window, which is defined as:
#' $w(n) = \alpha - \beta\cos\frac{2\pi n}{N-1}$, where $\alpha=0.54$ and $\beta=0.46$
fs = 20
f = digitalfilter(Lowpass(5, fs = fs), FIRWindow(hamming(61)))
w = linspace(0, pi, 1024)
h = FIRfreqz(f, w)
#' ## Plot the frequency and impulse response
h_db = log10(abs(h))
ws = w/pi*(fs/2)
#' The next code chunk is executed in term mode, see the [script](FIR_design.jl) for syntax.
#+ term=true
plot(y = h_db, x = ws, Geom.line,
Guide.xlabel("Frequency (Hz)"), Guide.ylabel("Magnitude (db)"))
#' And again with default options
h_phase = unwrap(-atan2(imag(h),real(h)))
plot(y = h_phase, x = ws, Geom.line,
Guide.xlabel("Frequency (Hz)"), Guide.ylabel("Phase (radians)"))

View File

@ -1,58 +1,42 @@
---
title: FIR filter design with Julia
author: Matti Pastell
date: 21th April 2016
---
% FIR filter design with Julia
% Matti Pastell
% 21th April 2016
# Introduction
This an example of a julia script that can be published using
[Weave](http://weavejl.mpastell.com/dev/usage/).
[Weave](http://mpastell.github.io/Weave.jl/latest/usage/).
The script can be executed normally using Julia
or published to HTML or pdf with Weave.
Text is written in markdown in lines starting with "`#'` " and code
is executed and results are included in the published document.
Notice that you don't need to define chunk options, but you can using
`#+`. just before code e.g. `#+ term=True, caption='Fancy plots.'`.
If you're viewing the published version have a look at the
[source](FIR_design_plots.jl) to see the markup.
<!-- this setup dependencies, but doesn't appear in the generated document -->
```julia; echo = false; results = "hidden"
using Pkg
"Plots" ∉ keys(Pkg.project().dependencies) && Pkg.add("Plots")
"DSP" ∉ keys(Pkg.project().dependencies) && Pkg.add("DSP")
```
[source](FIR_design.jl) to see the markup.
# FIR Filter Design
We'll implement lowpass, highpass and ' bandpass FIR filters. If
you want to read more about DSP I highly recommend [The Scientist
and Engineer's Guide to Digital Signal
Processing](http://www.dspguide.com/) which is freely available
online.
## Calculating frequency response
DSP.jl package doesn't (yet) have a method to calculate the
the frequency response of a FIR filter so we define it:
```julia
using Plots, DSP
gr()
function FIRfreqz(b::Array, w = range(0, stop=π, length=1024))
~~~~{.julia}
using Gadfly, DSP
function FIRfreqz(b::Array, w = linspace(0, π, 1024))
n = length(w)
h = Array{ComplexF32}(undef, n)
h = Array{Complex64}(n)
sw = 0
for i = 1:n
for j = 1:length(b)
@ -63,11 +47,13 @@ function FIRfreqz(b::Array, w = range(0, stop=π, length=1024))
end
return h
end
```
~~~~~~~~~~~~~
## Design Lowpass FIR filter
Designing a lowpass FIR filter is very simple to do with DSP.jl, all you
need to do is to define the window length, cut off frequency and the
window. We will define a lowpass filter with cut off frequency at 5Hz for a signal
@ -75,32 +61,51 @@ sampled at 20 Hz.
We will use the Hamming window, which is defined as:
$w(n) = \alpha - \beta\cos\frac{2\pi n}{N-1}$, where $\alpha=0.54$ and $\beta=0.46$
```julia
~~~~{.julia}
fs = 20
f = digitalfilter(Lowpass(5, fs = fs), FIRWindow(hamming(61)))
w = range(0, stop=pi, length=1024)
w = linspace(0, pi, 1024)
h = FIRfreqz(f, w)
```
~~~~~~~~~~~~~
## Plot the frequency and impulse response
~~~~{.julia}
h_db = log10(abs(h))
ws = w/pi*(fs/2)
~~~~~~~~~~~~~
The next code chunk is executed in term mode, see the [script](FIR_design.jl) for syntax.
```julia; term=true
h_db = log10.(abs.(h));
ws = w/pi*(fs/2)
```
~~~~{.julia}
julia> plot(y = h_db, x = ws, Geom.line,
Guide.xlabel("Frequency (Hz)"), Guide.ylabel("Magnitude (db)"))
~~~~~~~~~~~~~
![](figures/FIR_design_4_1.png)\
```julia
plot(ws, h_db,
xlabel = "Frequency (Hz)", ylabel = "Magnitude (db)")
```
And again with default options
```julia
h_phase = unwrap(-atan.(imag.(h),real.(h)))
plot(ws, h_phase,
xlabel = "Frequency (Hz)", ylabel = "Phase (radians)")
```
~~~~{.julia}
h_phase = unwrap(-atan2(imag(h),real(h)))
plot(y = h_phase, x = ws, Geom.line,
Guide.xlabel("Frequency (Hz)"), Guide.ylabel("Phase (radians)"))
~~~~~~~~~~~~~
![](figures/FIR_design_5_1.png)\

View File

@ -0,0 +1,6 @@
Functions:
<<>>=
f(x)=x^2
println(f(2))
@

View File

@ -0,0 +1,13 @@
Functions:
~~~~{.julia}
f(x)=x^2
println(f(2))
~~~~~~~~~~~~~
~~~~
4
~~~~

View File

@ -0,0 +1,30 @@
<<>>=
using Gadfly
print(1:10)
plot(x = 1:10)
@
<<>>=
import Weave
Weave.set_chunk_defaults(Dict{Symbol, Any}(
:out_width => "\\0.5linewidth",
:results => "tex"
))
@
<<>>=
print(1:10)
plot(x = 1:10)
@
<<echo = false>>=
Weave.restore_chunk_defaults()
@
<<>>=
print(1:10)
plot(x = 1:10)
@

View File

@ -0,0 +1,43 @@
\begin{juliacode}
using Gadfly
print(1:10)
\end{juliacode}
\begin{juliaout}
1:10
\end{juliaout}
\begin{juliacode}
plot(x = 1:10)
\end{juliacode}
\includegraphics[width=\linewidth]{figures/default_opts_1_1.pdf}
\begin{juliacode}
import Weave
Weave.set_chunk_defaults(Dict{Symbol, Any}(
:out_width => "\\0.5linewidth",
:results => "tex"
))
\end{juliacode}
\begin{juliacode}
print(1:10)
\end{juliacode}
1:10
\begin{juliacode}
plot(x = 1:10)
\end{juliacode}
\includegraphics[width=\0.5linewidth]{figures/default_opts_3_1.pdf}
\begin{juliacode}
print(1:10)
\end{juliacode}
\begin{juliaout}
1:10
\end{juliaout}
\begin{juliacode}
plot(x = 1:10)
\end{juliacode}
\includegraphics[width=\linewidth]{figures/default_opts_5_1.pdf}

View File

@ -0,0 +1,29 @@
<<fig_cap="sin(x) function."; label="sin_fun"; fig_pos="ht">>=
using Gadfly
x = linspace(0, 2π, 200)
plot(x=x, y = sin(x), Geom.line)
@
<<echo=false; fig_cap="cos(x) function."; dpi=200>>=
plot(x=x, y = cos(x), Geom.line)
@
<<echo=false; label="cos2_fun">>=
plot(x=x, y = cos(2x), Geom.line)
@
<<term=true;fig_width=5>>=
x = linspace(0, 2π, 200)
plot(x=x, y = sin(x), Geom.line)
y = 20
plot(x=x, y = cos(x), Geom.line)
@
<<out_width="15cm">>=
x = linspace(0, 2π, 200)
plot(x=x, y = sin(x), Geom.line)
y = 20
plot(x=x, y = cos(x), Geom.line)
@

View File

@ -0,0 +1,51 @@
````julia
using Gadfly
x = linspace(0, 2π, 200)
plot(x=x, y = sin(x), Geom.line)
````
![sin(x) function.](figures/gadfly_formats_test_sin_fun_1.js.svg)
![cos(x) function.](figures/gadfly_formats_test_2_1.js.svg)
![](figures/gadfly_formats_test_cos2_fun_1.js.svg)
````julia
julia> x = linspace(0, 2π, 200)
200-element LinSpace{Float64}:
0.0,0.0315738,0.0631476,0.0947214,0.126295,…,6.18846,6.22004,6.25161,6.28319
julia> plot(x=x, y = sin(x), Geom.line)
````
![](figures/gadfly_formats_test_4_1.js.svg)
````julia
julia> y = 20
20
julia> plot(x=x, y = cos(x), Geom.line)
````
![](figures/gadfly_formats_test_4_2.js.svg)
````julia
x = linspace(0, 2π, 200)
plot(x=x, y = sin(x), Geom.line)
````
![](figures/gadfly_formats_test_5_1.js.svg)
````julia
y = 20
plot(x=x, y = cos(x), Geom.line)
````
![](figures/gadfly_formats_test_5_2.js.svg)

View File

@ -0,0 +1,65 @@
````julia
using Gadfly
x = linspace(0, 2π, 200)
plot(x=x, y = sin(x), Geom.line)
````
![sin(x) function.][figures/gadfly_formats_test_sin_fun_1.png]
[figures/gadfly_formats_test_sin_fun_1.png]: figures/gadfly_formats_test_sin_fun_1.png
![cos(x) function.][figures/gadfly_formats_test_2_1.png]
[figures/gadfly_formats_test_2_1.png]: figures/gadfly_formats_test_2_1.png
![][figures/gadfly_formats_test_cos2_fun_1.png]
[figures/gadfly_formats_test_cos2_fun_1.png]: figures/gadfly_formats_test_cos2_fun_1.png
````julia
julia> x = linspace(0, 2π, 200)
200-element LinSpace{Float64}:
0.0,0.0315738,0.0631476,0.0947214,0.126295,…,6.18846,6.22004,6.25161,6.28319
julia> plot(x=x, y = sin(x), Geom.line)
````
![][figures/gadfly_formats_test_4_1.png]
[figures/gadfly_formats_test_4_1.png]: figures/gadfly_formats_test_4_1.png
````julia
julia> y = 20
20
julia> plot(x=x, y = cos(x), Geom.line)
````
![][figures/gadfly_formats_test_4_2.png]
[figures/gadfly_formats_test_4_2.png]: figures/gadfly_formats_test_4_2.png
````julia
x = linspace(0, 2π, 200)
plot(x=x, y = sin(x), Geom.line)
````
![][figures/gadfly_formats_test_5_1.png]
[figures/gadfly_formats_test_5_1.png]: figures/gadfly_formats_test_5_1.png width=15cm
````julia
y = 20
plot(x=x, y = cos(x), Geom.line)
````
![][figures/gadfly_formats_test_5_2.png]
[figures/gadfly_formats_test_5_2.png]: figures/gadfly_formats_test_5_2.png width=15cm

View File

@ -0,0 +1,56 @@
~~~~{.julia}
using Gadfly
x = linspace(0, 2π, 200)
plot(x=x, y = sin(x), Geom.line)
~~~~~~~~~~~~~
![sin(x) function.](figures/gadfly_formats_test_sin_fun_1.png)
![cos(x) function.](figures/gadfly_formats_test_2_1.png)
![](figures/gadfly_formats_test_cos2_fun_1.png)\
~~~~{.julia}
julia> x = linspace(0, 2π, 200)
200-element LinSpace{Float64}:
0.0,0.0315738,0.0631476,0.0947214,0.126295,…,6.18846,6.22004,6.25161,6.28319
julia> plot(x=x, y = sin(x), Geom.line)
~~~~~~~~~~~~~
![](figures/gadfly_formats_test_4_1.png)\
~~~~{.julia}
julia> y = 20
20
julia> plot(x=x, y = cos(x), Geom.line)
~~~~~~~~~~~~~
![](figures/gadfly_formats_test_4_2.png)\
~~~~{.julia}
x = linspace(0, 2π, 200)
plot(x=x, y = sin(x), Geom.line)
~~~~~~~~~~~~~
![](figures/gadfly_formats_test_5_1.png){width=15cm}\
~~~~{.julia}
y = 20
plot(x=x, y = cos(x), Geom.line)
~~~~~~~~~~~~~
![](figures/gadfly_formats_test_5_2.png){width=15cm}\

View File

@ -0,0 +1,50 @@
\begin{juliacode}
using Gadfly
x = linspace(0, 2π, 200)
plot(x=x, y = sin(x), Geom.line)
\end{juliacode}
\begin{figure}[ht]
\center
\includegraphics[width=\linewidth]{figures/gadfly_formats_test_sin_fun_1.ps}
\caption{sin(x) function.}
\label{fig:sin_fun}
\end{figure}
\begin{figure}[htpb]
\center
\includegraphics[width=\linewidth]{figures/gadfly_formats_test_2_1.ps}
\caption{cos(x) function.}
\end{figure}
\includegraphics[width=\linewidth]{figures/gadfly_formats_test_cos2_fun_1.ps}
\begin{juliaterm}
julia> x = linspace(0, 2π, 200)
200-element LinSpace{Float64}:
0.0,0.0315738,0.0631476,0.0947214,0.126295,…,6.18846,6.22004,6.25161,6.28319
julia> plot(x=x, y = sin(x), Geom.line)
\end{juliaterm}
\includegraphics[width=\linewidth]{figures/gadfly_formats_test_4_1.ps}
\begin{juliaterm}
julia> y = 20
20
julia> plot(x=x, y = cos(x), Geom.line)
\end{juliaterm}
\includegraphics[width=\linewidth]{figures/gadfly_formats_test_4_2.ps}
\begin{juliacode}
x = linspace(0, 2π, 200)
plot(x=x, y = sin(x), Geom.line)
\end{juliacode}
\includegraphics[width=15cm]{figures/gadfly_formats_test_5_1.ps}
\begin{juliacode}
y = 20
plot(x=x, y = cos(x), Geom.line)
\end{juliacode}
\includegraphics[width=15cm]{figures/gadfly_formats_test_5_2.ps}

View File

@ -0,0 +1,74 @@
.. code-block:: julia
using Gadfly
x = linspace(0, 2π, 200)
plot(x=x, y = sin(x), Geom.line)
.. figure:: figures/gadfly_formats_test_sin_fun_1.png
:width: 15 cm
sin(x) function.
.. figure:: figures/gadfly_formats_test_2_1.png
:width: 15 cm
cos(x) function.
.. image:: figures/gadfly_formats_test_cos2_fun_1.png
:width: 15 cm
.. code-block:: julia
julia> x = linspace(0, 2π, 200)
200-element LinSpace{Float64}:
0.0,0.0315738,0.0631476,0.0947214,0.126295,…,6.18846,6.22004,6.25161,6.28319
julia> plot(x=x, y = sin(x), Geom.line)
.. image:: figures/gadfly_formats_test_4_1.png
:width: 15 cm
.. code-block:: julia
julia> y = 20
20
julia> plot(x=x, y = cos(x), Geom.line)
.. image:: figures/gadfly_formats_test_4_2.png
:width: 15 cm
.. code-block:: julia
x = linspace(0, 2π, 200)
plot(x=x, y = sin(x), Geom.line)
.. image:: figures/gadfly_formats_test_5_1.png
:width: 15cm
.. code-block:: julia
y = 20
plot(x=x, y = cos(x), Geom.line)
.. image:: figures/gadfly_formats_test_5_2.png
:width: 15cm

View File

@ -0,0 +1,50 @@
\begin{juliacode}
using Gadfly
x = linspace(0, 2π, 200)
plot(x=x, y = sin(x), Geom.line)
\end{juliacode}
\begin{figure}[ht]
\center
\includegraphics[width=\linewidth]{figures/gadfly_formats_test_sin_fun_1.pdf}
\caption{sin(x) function.}
\label{fig:sin_fun}
\end{figure}
\begin{figure}[htpb]
\center
\includegraphics[width=\linewidth]{figures/gadfly_formats_test_2_1.pdf}
\caption{cos(x) function.}
\end{figure}
\includegraphics[width=\linewidth]{figures/gadfly_formats_test_cos2_fun_1.pdf}
\begin{juliaterm}
julia> x = linspace(0, 2π, 200)
200-element LinSpace{Float64}:
0.0,0.0315738,0.0631476,0.0947214,0.126295,…,6.18846,6.22004,6.25161,6.28319
julia> plot(x=x, y = sin(x), Geom.line)
\end{juliaterm}
\includegraphics[width=\linewidth]{figures/gadfly_formats_test_4_1.pdf}
\begin{juliaterm}
julia> y = 20
20
julia> plot(x=x, y = cos(x), Geom.line)
\end{juliaterm}
\includegraphics[width=\linewidth]{figures/gadfly_formats_test_4_2.pdf}
\begin{juliacode}
x = linspace(0, 2π, 200)
plot(x=x, y = sin(x), Geom.line)
\end{juliacode}
\includegraphics[width=15cm]{figures/gadfly_formats_test_5_1.pdf}
\begin{juliacode}
y = 20
plot(x=x, y = cos(x), Geom.line)
\end{juliacode}
\includegraphics[width=15cm]{figures/gadfly_formats_test_5_2.pdf}

View File

@ -0,0 +1,56 @@
~~~~{.julia}
using Gadfly
x = linspace(0, 2π, 200)
plot(x=x, y = sin(x), Geom.line)
~~~~~~~~~~~~~
![sin(x) function.](figures/gadfly_formats_test_sin_fun_1.svg)
![cos(x) function.](figures/gadfly_formats_test_2_1.svg)
![](figures/gadfly_formats_test_cos2_fun_1.svg)\
~~~~{.julia}
julia> x = linspace(0, 2π, 200)
200-element LinSpace{Float64}:
0.0,0.0315738,0.0631476,0.0947214,0.126295,…,6.18846,6.22004,6.25161,6.28319
julia> plot(x=x, y = sin(x), Geom.line)
~~~~~~~~~~~~~
![](figures/gadfly_formats_test_4_1.svg)\
~~~~{.julia}
julia> y = 20
20
julia> plot(x=x, y = cos(x), Geom.line)
~~~~~~~~~~~~~
![](figures/gadfly_formats_test_4_2.svg)\
~~~~{.julia}
x = linspace(0, 2π, 200)
plot(x=x, y = sin(x), Geom.line)
~~~~~~~~~~~~~
![](figures/gadfly_formats_test_5_1.svg){width=15cm}\
~~~~{.julia}
y = 20
plot(x=x, y = cos(x), Geom.line)
~~~~~~~~~~~~~
![](figures/gadfly_formats_test_5_2.svg){width=15cm}\

View File

@ -0,0 +1,50 @@
\begin{juliacode}
using Gadfly
x = linspace(0, 2π, 200)
plot(x=x, y = sin(x), Geom.line)
\end{juliacode}
\begin{figure}[ht]
\center
\resizebox{\linewidth}{!}{\input{figures/gadfly_formats_test_sin_fun_1.tex}}
\caption{sin(x) function.}
\label{fig:sin_fun}
\end{figure}
\begin{figure}[htpb]
\center
\resizebox{\linewidth}{!}{\input{figures/gadfly_formats_test_2_1.tex}}
\caption{cos(x) function.}
\end{figure}
\resizebox{\linewidth}{!}{\input{figures/gadfly_formats_test_cos2_fun_1.tex}}
\begin{juliaterm}
julia> x = linspace(0, 2π, 200)
200-element LinSpace{Float64}:
0.0,0.0315738,0.0631476,0.0947214,0.126295,…,6.18846,6.22004,6.25161,6.28319
julia> plot(x=x, y = sin(x), Geom.line)
\end{juliaterm}
\resizebox{\linewidth}{!}{\input{figures/gadfly_formats_test_4_1.tex}}
\begin{juliaterm}
julia> y = 20
20
julia> plot(x=x, y = cos(x), Geom.line)
\end{juliaterm}
\resizebox{\linewidth}{!}{\input{figures/gadfly_formats_test_4_2.tex}}
\begin{juliacode}
x = linspace(0, 2π, 200)
plot(x=x, y = sin(x), Geom.line)
\end{juliacode}
\resizebox{15cm}{!}{\input{figures/gadfly_formats_test_5_1.tex}}
\begin{juliacode}
y = 20
plot(x=x, y = cos(x), Geom.line)
\end{juliacode}
\resizebox{15cm}{!}{\input{figures/gadfly_formats_test_5_2.tex}}

View File

@ -0,0 +1,30 @@
# Gadfly
```{julia;term=true;fig_width=5}
using Gadfly
x = linspace(0, 2π, 200)
plot(x=x, y = sin(x), Geom.line)
y = 20
plot(x=x, y = cos(x), Geom.line)
```
```julia
x = linspace(0, 200)
println(x)
```
~~~{julia;term=true;fig_width=5}
using Gadfly
x = linspace(0, 2π, 200)
plot(x=x, y = sin(x), Geom.line)
y = 20
plot(x=x, y = cos(x), Geom.line)
~~~
~~~julia
x = linspace(0, 200)
println(x)
~~~

View File

@ -0,0 +1,76 @@
# Gadfly
````julia
julia> using Gadfly
julia> x = linspace(0, 2π, 200)
200-element LinSpace{Float64}:
0.0,0.0315738,0.0631476,0.0947214,0.126295,…,6.18846,6.22004,6.25161,6.28319
julia> plot(x=x, y = sin(x), Geom.line)
````
![](figures/gadfly_markdown_test_1_1.png)
````julia
julia> y = 20
20
julia> plot(x=x, y = cos(x), Geom.line)
````
![](figures/gadfly_markdown_test_1_2.png)
````julia
x = linspace(0, 200)
println(x)
````
````
linspace(0.0,200.0,50)
````
````julia
julia> using Gadfly
julia> x = linspace(0, 2π, 200)
200-element LinSpace{Float64}:
0.0,0.0315738,0.0631476,0.0947214,0.126295,…,6.18846,6.22004,6.25161,6.28319
julia> plot(x=x, y = sin(x), Geom.line)
````
![](figures/gadfly_markdown_test_3_1.png)
````julia
julia> y = 20
20
julia> plot(x=x, y = cos(x), Geom.line)
````
![](figures/gadfly_markdown_test_3_2.png)
````julia
x = linspace(0, 200)
println(x)
````
````
linspace(0.0,200.0,50)
````

Some files were not shown because too many files have changed in this diff Show More