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 @@ -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
your package documentation.
Literate can generate markdown pages
(for e.g. [Documenter.jl](https://github.com/JuliaDocs/Documenter.jl)), and
[Jupyter notebooks](http://jupyter.org/), from the same source file. There is also
an option to "clean" the source from all metadata, and produce a 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.
Literate can generate multiple outputs from a single source file: Markdown pages
(for e.g. [Documenter.jl](https://github.com/JuliaDocs/Documenter.jl)),
[Jupyter notebooks](http://jupyter.org/), and
[Pluto notebooks](https://github.com/fonsp/Pluto.jl).
There is also an option to "clean" the source from all metadata, and produce a
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 source file](https://github.com/fredrikekre/Literate.jl/blob/master/examples/README.jl)

17
docs/src/outputformats.md

@ -80,16 +80,17 @@ Literate.markdown @@ -80,16 +80,17 @@ Literate.markdown
Literate can output markdown in different flavors. The flavor is specified using the
`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).
- `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/).
## [**4.2.** Notebook output](@id Notebook-output)
Notebook output is generated by [`Literate.notebook`](@ref). The (default) notebook output
of the source snippet can be seen here: [notebook.ipynb](generated/notebook.ipynb).
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),
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,
and the code lines have been placed in code cells. By default the notebook
@ -103,6 +104,14 @@ output of [`Literate.notebook`](@ref). @@ -103,6 +104,14 @@ output of [`Literate.notebook`](@ref).
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
Jupyter notebook cells (both code cells and markdown cells) can contain metadata. This is enabled

13
examples/README.jl

@ -8,12 +8,13 @@ @@ -8,12 +8,13 @@
# The main purpose is to facilitate writing Julia examples/tutorials that can be included in
# your package documentation.
# Literate can generate markdown pages
# (for e.g. [Documenter.jl](https://github.com/JuliaDocs/Documenter.jl)), and
# [Jupyter notebooks](http://jupyter.org/), from the same source file. There is also
# an option to "clean" the source from all metadata, and produce a 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.
# Literate can generate multiple outputs from a single source file: Markdown pages
# (for e.g. [Documenter.jl](https://github.com/JuliaDocs/Documenter.jl)),
# [Jupyter notebooks](http://jupyter.org/), and
# [Pluto notebooks](https://github.com/fonsp/Pluto.jl).
# There is also an option to "clean" the source from all metadata, and produce a
# 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 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 @@ -16,6 +16,8 @@ struct DefaultFlavor <: AbstractFlavor end
struct DocumenterFlavor <: AbstractFlavor end
struct CommonMarkFlavor <: AbstractFlavor end
struct FranklinFlavor <: AbstractFlavor end
struct JupyterFlavor <: AbstractFlavor end
struct PlutoFlavor <: AbstractFlavor end
# # Some simple rules:
#
@ -251,7 +253,7 @@ function create_configuration(inputfile; user_config, user_kwargs, type=nothing) @@ -251,7 +253,7 @@ function create_configuration(inputfile; user_config, user_kwargs, type=nothing)
cfg["name"] = filename(inputfile)
cfg["preprocess"] = identity
cfg["postprocess"] = identity
cfg["flavor"] = type === (:md) ? DocumenterFlavor() : DefaultFlavor()
cfg["flavor"] = type === (:md) ? DocumenterFlavor() : type === (:nb) ? JupyterFlavor() : DefaultFlavor()
cfg["credit"] = true
cfg["mdstrings"] = false
cfg["keep_comments"] = false
@ -348,14 +350,17 @@ Available options: @@ -348,14 +350,17 @@ Available options:
`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.
- `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
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()`
and `````"````julia" => "````"````` otherwise): Pair containing opening and closing
code fence for wrapping code blocks.
- `flavor` (default: `Literate.DocumenterFlavor()`) Output flavor for markdown, see
[Markdown flavors](@ref). Only applicable for `Literate.markdown`.
- `flavor` (default: `Literate.DocumenterFlavor()` for `Literate.markdown` and
`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]
(https://juliadocs.github.io/Documenter.jl/). Unused if `repo_root_url`/
`nbviewer_root_url`/`binder_root_url` are set.
@ -393,7 +398,9 @@ function preprocessor(inputfile, outputdir; user_config, user_kwargs, type) @@ -393,7 +398,9 @@ function preprocessor(inputfile, outputdir; user_config, user_kwargs, type)
# Add some information for passing around Literate methods
config["literate_inputfile"] = inputfile
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
content = read(inputfile, String)
@ -610,14 +617,15 @@ function notebook(inputfile, outputdir=pwd(); config::Dict=Dict(), kwargs...) @@ -610,14 +617,15 @@ function notebook(inputfile, outputdir=pwd(); config::Dict=Dict(), kwargs...)
preprocessor(inputfile, outputdir; user_config=config, user_kwargs=kwargs, type=:nb)
# create the notebook
nb = jupyter_notebook(chunks, config)
nb = create_notebook(config["flavor"]::AbstractFlavor, chunks, config)
# 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
end
function jupyter_notebook(chunks, config)
function create_notebook(::JupyterFlavor, chunks, config)
nb = Dict()
nb["nbformat"] = JUPYTER_VERSION.major
nb["nbformat_minor"] = JUPYTER_VERSION.minor
@ -742,6 +750,93 @@ function execute_notebook(nb; inputfile::String="<unknown>") @@ -742,6 +750,93 @@ function execute_notebook(nb; inputfile::String="<unknown>")
return nb
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
function sandbox()
m = Module(gensym())

Loading…
Cancel
Save