Browse Source

Merge 54bc6119c6 into 8282505a13

pull/120/merge
Fredrik Ekre 4 years ago committed by GitHub
parent
commit
d98b1888d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 13
      README.md
  2. 17
      docs/src/outputformats.md
  3. 13
      examples/README.jl
  4. 113
      src/Literate.jl

13
README.md

@ -8,12 +8,13 @@ Literate is a package for [Literate Programming](https://en.wikipedia.org/wiki/L
The main purpose is to facilitate writing Julia examples/tutorials that can be included in The main purpose is to facilitate writing Julia examples/tutorials that can be included in
your package documentation. your package documentation.
Literate can generate markdown pages Literate can generate multiple outputs from a single source file: Markdown pages
(for e.g. [Documenter.jl](https://github.com/JuliaDocs/Documenter.jl)), and (for e.g. [Documenter.jl](https://github.com/JuliaDocs/Documenter.jl)),
[Jupyter notebooks](http://jupyter.org/), from the same source file. There is also [Jupyter notebooks](http://jupyter.org/), and
an option to "clean" the source from all metadata, and produce a pure Julia script. [Pluto notebooks](https://github.com/fonsp/Pluto.jl).
Using a single source file for multiple purposes reduces maintenance, and makes sure There is also an option to "clean" the source from all metadata, and produce a
your different output formats are synced with each other. pure Julia script. Using a single source file for multiple purposes reduces maintenance,
and makes sure your different output formats are synced with each other.
This README was generated directly from This README was generated directly from
[this source file](https://github.com/fredrikekre/Literate.jl/blob/master/examples/README.jl) [this source file](https://github.com/fredrikekre/Literate.jl/blob/master/examples/README.jl)

17
docs/src/outputformats.md

@ -80,16 +80,17 @@ Literate.markdown
Literate can output markdown in different flavors. The flavor is specified using the Literate can output markdown in different flavors. The flavor is specified using the
`flavor` keyword argument. The following flavors are currently supported: `flavor` keyword argument. The following flavors are currently supported:
- `flavor = Literate.DocumenterFlavor()` this is the default flavor and the output is - `flavor = Literate.DocumenterFlavor()`: this is the default flavor and the output is
meant to be used as input to [Documenter.jl](https://github.com/JuliaDocs/Documenter.jl). meant to be used as input to [Documenter.jl](https://github.com/JuliaDocs/Documenter.jl).
- `flavor = Literate.FranklinFlavor()` this outputs markdown meant to be used as input - `flavor = Literate.FranklinFlavor()`: this outputs markdown meant to be used as input
to [Franklin.jl](https://franklinjl.org/). to [Franklin.jl](https://franklinjl.org/).
## [**4.2.** Notebook output](@id Notebook-output) ## [**4.2.** Notebook output](@id Notebook-output)
Notebook output is generated by [`Literate.notebook`](@ref). The (default) notebook output Notebook output is generated by [`Literate.notebook`](@ref). The Jupyter notebook output
of the source snippet can be seen here: [notebook.ipynb](generated/notebook.ipynb). of the source snippet can be seen here: [notebook.ipynb](generated/notebook.ipynb),
and the Pluto notebook output can be seen here: [notebook.jl](generated/notebook.jl).
We note that lines starting with `# ` are placed in markdown cells, We note that lines starting with `# ` are placed in markdown cells,
and the code lines have been placed in code cells. By default the notebook and the code lines have been placed in code cells. By default the notebook
@ -103,6 +104,14 @@ output of [`Literate.notebook`](@ref).
Literate.notebook Literate.notebook
``` ```
### Notebook flavors
Literate can output both Jupyter notebooks and Pluto notebooks. The flavor is specified
using the `flavor` keyword argument:
- `flavor = Literate.JupyterFlavor()` (default)
- `flavor = Literate.PlutoFlavor()`
### Notebook metadata ### Notebook metadata
Jupyter notebook cells (both code cells and markdown cells) can contain metadata. This is enabled Jupyter notebook cells (both code cells and markdown cells) can contain metadata. This is enabled

13
examples/README.jl

@ -8,12 +8,13 @@
# The main purpose is to facilitate writing Julia examples/tutorials that can be included in # The main purpose is to facilitate writing Julia examples/tutorials that can be included in
# your package documentation. # your package documentation.
# Literate can generate markdown pages # Literate can generate multiple outputs from a single source file: Markdown pages
# (for e.g. [Documenter.jl](https://github.com/JuliaDocs/Documenter.jl)), and # (for e.g. [Documenter.jl](https://github.com/JuliaDocs/Documenter.jl)),
# [Jupyter notebooks](http://jupyter.org/), from the same source file. There is also # [Jupyter notebooks](http://jupyter.org/), and
# an option to "clean" the source from all metadata, and produce a pure Julia script. # [Pluto notebooks](https://github.com/fonsp/Pluto.jl).
# Using a single source file for multiple purposes reduces maintenance, and makes sure # There is also an option to "clean" the source from all metadata, and produce a
# your different output formats are synced with each other. # pure Julia script. Using a single source file for multiple purposes reduces maintenance,
# and makes sure your different output formats are synced with each other.
# #
# This README was generated directly from # This README was generated directly from
# [this source file](https://github.com/fredrikekre/Literate.jl/blob/master/examples/README.jl) # [this source file](https://github.com/fredrikekre/Literate.jl/blob/master/examples/README.jl)

113
src/Literate.jl

@ -16,6 +16,8 @@ struct DefaultFlavor <: AbstractFlavor end
struct DocumenterFlavor <: AbstractFlavor end struct DocumenterFlavor <: AbstractFlavor end
struct CommonMarkFlavor <: AbstractFlavor end struct CommonMarkFlavor <: AbstractFlavor end
struct FranklinFlavor <: AbstractFlavor end struct FranklinFlavor <: AbstractFlavor end
struct JupyterFlavor <: AbstractFlavor end
struct PlutoFlavor <: AbstractFlavor end
# # Some simple rules: # # Some simple rules:
# #
@ -251,7 +253,7 @@ function create_configuration(inputfile; user_config, user_kwargs, type=nothing)
cfg["name"] = filename(inputfile) cfg["name"] = filename(inputfile)
cfg["preprocess"] = identity cfg["preprocess"] = identity
cfg["postprocess"] = identity cfg["postprocess"] = identity
cfg["flavor"] = type === (:md) ? DocumenterFlavor() : DefaultFlavor() cfg["flavor"] = type === (:md) ? DocumenterFlavor() : type === (:nb) ? JupyterFlavor() : DefaultFlavor()
cfg["credit"] = true cfg["credit"] = true
cfg["mdstrings"] = false cfg["mdstrings"] = false
cfg["keep_comments"] = false cfg["keep_comments"] = false
@ -348,14 +350,17 @@ Available options:
`This file was generated with Literate.jl ...` to the bottom of the page. If you find `This file was generated with Literate.jl ...` to the bottom of the page. If you find
Literate.jl useful then feel free to keep this. Literate.jl useful then feel free to keep this.
- `keep_comments` (default: `false`): When `true`, keeps markdown lines as comments in the - `keep_comments` (default: `false`): When `true`, keeps markdown lines as comments in the
output script. Only applicable for `Literate.script`. output script. Only applicable for [`Literate.script`](@ref)
- `execute` (default: `true` for notebook, `false` for markdown): Whether to execute and - `execute` (default: `true` for notebook, `false` for markdown): Whether to execute and
capture the output. Only applicable for `Literate.notebook` and `Literate.markdown`. capture the output. Only applicable for [`Literate.notebook`](@ref) and
[`Literate.markdown`](@ref).
- `codefence` (default: `````"````@example \$(name)" => "````"````` for `DocumenterFlavor()` - `codefence` (default: `````"````@example \$(name)" => "````"````` for `DocumenterFlavor()`
and `````"````julia" => "````"````` otherwise): Pair containing opening and closing and `````"````julia" => "````"````` otherwise): Pair containing opening and closing
code fence for wrapping code blocks. code fence for wrapping code blocks.
- `flavor` (default: `Literate.DocumenterFlavor()`) Output flavor for markdown, see - `flavor` (default: `Literate.DocumenterFlavor()` for `Literate.markdown` and
[Markdown flavors](@ref). Only applicable for `Literate.markdown`. `Literate.JupyterFlavor()` for `Literate.notebook`) Output flavor for markdown and
notebook output, see [Markdown flavors](@ref) and [Notebook flavors](@ref).
Not used for `Literate.script`.
- `devurl` (default: `"dev"`): URL for "in-development" docs, see [Documenter docs] - `devurl` (default: `"dev"`): URL for "in-development" docs, see [Documenter docs]
(https://juliadocs.github.io/Documenter.jl/). Unused if `repo_root_url`/ (https://juliadocs.github.io/Documenter.jl/). Unused if `repo_root_url`/
`nbviewer_root_url`/`binder_root_url` are set. `nbviewer_root_url`/`binder_root_url` are set.
@ -393,7 +398,9 @@ function preprocessor(inputfile, outputdir; user_config, user_kwargs, type)
# Add some information for passing around Literate methods # Add some information for passing around Literate methods
config["literate_inputfile"] = inputfile config["literate_inputfile"] = inputfile
config["literate_outputdir"] = outputdir config["literate_outputdir"] = outputdir
config["literate_ext"] = type === (:nb) ? ".ipynb" : ".$(type)" config["literate_ext"] = type === (:nb) ? (
config["flavor"]::AbstractFlavor isa JupyterFlavor ? ".ipynb" : ".jl") :
".$(type)"
# read content # read content
content = read(inputfile, String) content = read(inputfile, String)
@ -610,14 +617,15 @@ function notebook(inputfile, outputdir=pwd(); config::Dict=Dict(), kwargs...)
preprocessor(inputfile, outputdir; user_config=config, user_kwargs=kwargs, type=:nb) preprocessor(inputfile, outputdir; user_config=config, user_kwargs=kwargs, type=:nb)
# create the notebook # create the notebook
nb = jupyter_notebook(chunks, config) nb = create_notebook(config["flavor"]::AbstractFlavor, chunks, config)
# write to file # write to file
outputfile = write_result(nb, config; print = (io, c)->JSON.print(io, c, 1)) print = config["flavor"]::AbstractFlavor isa JupyterFlavor ? (io, c) -> JSON.print(io, c, 1) : Base.print
outputfile = write_result(nb, config; print = print)
return outputfile return outputfile
end end
function jupyter_notebook(chunks, config) function create_notebook(::JupyterFlavor, chunks, config)
nb = Dict() nb = Dict()
nb["nbformat"] = JUPYTER_VERSION.major nb["nbformat"] = JUPYTER_VERSION.major
nb["nbformat_minor"] = JUPYTER_VERSION.minor nb["nbformat_minor"] = JUPYTER_VERSION.minor
@ -742,6 +750,93 @@ function execute_notebook(nb; inputfile::String="<unknown>")
return nb return nb
end end
function create_notebook(::PlutoFlavor, chunks, config)
ionb = IOBuffer()
# Print header
write(ionb, """
### A Pluto.jl notebook ###
# v0.16.0
using Markdown
using InteractiveUtils
""")
# Print cells
uuids = Base.UUID[]
folds = Bool[]
default_fold = Dict{String,Bool}("markdown"=>true, "code"=>false) # toggleable ???
for (i, chunk) in enumerate(chunks)
io = IOBuffer()
# Jupyter style metadata # TODO: factor out, identical to jupyter notebook
chunktype = isa(chunk, MDChunk) ? "markdown" : "code"
fold = default_fold[chunktype]
if !isempty(chunk.lines) && line_is_nbmeta(chunk.lines[1])
@show chunk.lines
metatype, metadata = parse_nbmeta(chunk.lines[1])
metatype !== nothing && metatype != chunktype && error("specifying a different cell type is not supported")
popfirst!(chunk.lines)
fold = get(metadata, "fold", fold)
end
if isa(chunk, MDChunk)
if length(chunk.lines) == 1
line = escape_string(chunk.lines[1].second, '"')
write(io, "md\"", line, "\"\n")
else
write(io, "md\"\"\"\n")
for line in chunk.lines
write(io, line.second, '\n') # Skip indent
end
write(io, "\"\"\"\n")
end
content = String(take!(io))
else # isa(chunk, CodeChunk)
for line in chunk.lines
write(io, line, '\n')
end
content = String(take!(io))
# Compute number of expressions in the code block and perhaps wrap in begin/end
nexprs, idx = 0, 1
while true
ex, idx = Meta.parse(content, idx)
ex === nothing && break
nexprs += 1
end
if nexprs > 1
io = IOBuffer()
print(io, "begin\n")
foreach(l -> print(io, " ", l, '\n'), eachline(IOBuffer(content)))
print(io, "end\n")
content = String(take!(io))
end
end
uuid = uuid4(content, i)
push!(uuids, uuid)
push!(folds, fold)
print(ionb, "# ╔═╡ ", uuid, '\n')
write(ionb, content, '\n')
end
# Print cell order
print(ionb, "# ╔═╡ Cell order:\n")
foreach(((x, f),) -> print(ionb, "# $(f ? "╟─" : "╠═")", x, '\n'), zip(uuids, folds))
# custom post-processing from user
nb = config["postprocess"](String(take!(ionb)))
return nb
end
# UUID v4 from cell content and cell number (to keep it somewhat stable)
function uuid4(c, n)
c, n = hash(c), hash(n)
u = (convert(UInt128, c) << 64) convert(UInt128, n)
u &= 0xffffffffffff0fff3fffffffffffffff
u |= 0x00000000000040008000000000000000
return Base.UUID(u)
end
# Create a sandbox module for evaluation # Create a sandbox module for evaluation
function sandbox() function sandbox()
m = Module(gensym()) m = Module(gensym())

Loading…
Cancel
Save