@def hascode = true
\blurb{Franklin makes it easy to insert code and the result of running the code; Julia code can be evaluated on the fly.}
\lineskip
\toc
As per Common Mark specifications, you have multiple ways of inserting code:
- inline code: you can use single backticks (`) or double backticks (``) (if the code contains single ticks) like so:
This is some `inline code` or ``inline ` code with a tick``.
Note: To use a backslash (\
) at the end of the inline code, you must include a trailing space to ensure the trailing tick is parsed correctly, eg `some code with \ `
. The trailing space will be trimmed in the rendered html.
- code blocks: it is recommended to use triple backticks (```) optionally followed by a language name for highlighting like so:
This is some julia code:
```julia
a = 2
@show a
```
- code blocks 2: you can also use indented code blocks (lines starting with four spaces or a tab) but fenced code blocks should be preferred and you now have to opt-in to use them by setting
@def indented_code = true
This is some code:
a = 2
@show a
Note: when either using indented code blocks or using fenced code blocks with no language name, then the code language for highlighting can be specified with the local page variable lang
i.e. @def lang = "julia"
(which is the default) or @def lang = ""
if you don't want the code to be highlighted.
When presenting code in a post, it's often convenient to have a way to check that the code works and that the output shown corresponds to the code. In Franklin, there are two approaches that help you with it:
@@tlist
- For Julia code, a live-evaluation of code blocks is supported,
- For all languages, you can run the script separately and use Franklin to insert the code file and/or the output generated by the code. @@
Note: Disabling code evaluation (e.g., when presenting incomplete code snippets in tutorials or courses that would error upon evaluation), can be specified with the local page variable noeval = true
(default false
).
Julia code blocks can be evaluated on the fly and their output is either displayed as code or re-interpreted as Markdown.
\note{ Evaluation time: when a code block is created or modified and the page is saved, it will trigger a page build that will wait for the evaluation of the code block to complete. So if your code block takes a long time to execute, the page will not be updated before that's done. That being said, if you don't modify the code block, it will only be executed once as the output is saved to a file. }
Code blocks that should not be evaluated should be added as per standard markdown, so for instance:
```julia
a = 10
```
Code blocks that should be evaluated should be added with julia:path/to/script
where path/to/script
indicates where the script corresponding to the code block will be saved (note: the given path must be in UNIX format even if you're on Windows)
```julia:./code/ex1
a = 10
@show a
```
What this will do is:
@@tlist
- write the code to the file
/assets/[subpath]/code/ex1.jl
- run the code and capture its output (
STDOUT
) and write it to/assets/[subpath]/code/output/ex1.out
@@
The [subpath]
here is the exact same sub-path structure as the page where the code block is inserted.
To clarify, let's say you wrote the above code block in
/folder1/page1.md
then with the syntax above, the script will be saved in
/__site/assets/folder1/code/ex1.jl
There are three ways you can specify where the script corresponding to a code block should be saved.
@@tlist
- relative to the page:
./[p]/script
is as above, it will write the code block to/assets/[subpath]/p/script.jl
wheresubpath
corresponds to the sub-path of the page where the code block is inserted (path below/src/
) - relative to the assets dir:
p/script
will write the code block to/assets/p/script.jl
- full path:
/p/script
will write the code block to/p/script.jl
@@
Note: when code blocks are evaluated, their output (STDOUT
) is captured and saved at [path]/output/script.out
where [path]
is what precedes script.jl
in the cases above.
Let's say you've added the following code block:
```julia:./code_pg1/ex1
using LinearAlgebra
a = [1, 2, 3]
@show dot(a, a)
```
In order to show the raw output (whatever was captured in STDOUT) as a code block, write
\output{./code_pg1/ex1}
which in the present example will introduce exactly the following HTML
<pre><code class="language-julia">dot(a, a) = 14</code></pre>
and will look like
dot(a, a) = 14
If you now change the vector a
in the code block, the page will be re-compiled with the code block re-evaluated and the new output will be shown.
If you would like the output to be re-interpeted by Franklin as text, you can use \textoutput
instead.
Here's an example:
```julia:./code_pg1/ex2
using Statistics
temps = (15, 15, 14, 16, 18, 19, 20, 12, 10, 24)
println("The _average_ temperature is **$(mean(temps))°C**.")
```
\textoutput{./code_pg1/ex2}
That code block and the \textoutput
command will appear as:
using Statistics
temps = (15, 15, 14, 16, 18, 19, 20, 12, 10, 24)
println("The _average_ temperature is **$(mean(temps))°C**.")
The average temperature is 16.3°C.
Finally if you want to show your code "notebook-style", i.e. both STDOUT and the result of the last line, use \show
:
```julia:ex_show
x = 5
println("hello")
x^2
```
\show{ex_show}
resulting in:
x = 5
println("hello")
x^2
\show{ex_show}
Sometimes you may want to run some lines but hide them from the presentation, for this just use # hide
at the end of the line (hide
is not case sensitive so # HiDe
would be fine too):
```julia:./code_pg1/ex1
using LinearAlgebra # hide
a = [1, 2, 3]
@show dot(a, a)
```
You could also hide the entire code block if you only care about the output, for this put a # hideall
on any line:
```julia:./code_pg1/ex2
#hideall
using Statistics
temps = (15, 15, 14, 16, 18, 19, 20, 12, 10, 24)
println("The _average_ temperature is **$(mean(temps))°C**.")
```
\textoutput{./code_pg1/ex2}
Which will appear as just:
The average temperature is 16.3°C.
It can be convenient to set up your website as you would a Julia environment: activating it and adding the packages that you will use in code blocks.
In order to do this, just activate the environment as you would otherwise, this will generate a Project.toml
which will subsequently be used by Franklin without you having to worry about it.
For instance, let's say that you want to use PyCall
in some code blocks, then before starting the Franklin server do
(1.x) pkg> activate .
(myWebsite) pkg> add PyCall
once that's done, if you now start the server, Franklin will write
julia> serve()
Activating environment at `~/Desktop/myWebsite/Project.toml`
In other words, whenever you start the server, Franklin will now activate the environment with that Project.toml
.
This is particularly useful if you intend to write a tutorial website (for a live example of this, see the DataScienceTutorials).
Using the machinery introduced above, you can also evaluate code that generates a plot which you can then include on the page.
In the example below, PyPlot
is used but you could do something similar with other frameworks.
Assuming you've added PyPlot
to your environment, this markdown
```julia:pyplot1
using PyPlot
figure(figsize=(8, 6))
x = range(-2, 2, length=500)
for α in 1:5
plot(x, sinc.(α .* x))
end
savefig(joinpath(@OUTPUT, "sinc.svg")) # hide
```
\fig{sinc}
will give:
using PyPlot
figure(figsize=(8, 6))
x = range(-2, 2, length=500)
for α in 1:5
plot(x, sinc.(α .* x))
end
savefig(joinpath(@OUTPUT, "sinc.svg")) # hide
\fig{sinc}
Note: observe that here everything is done with relative paths, pyplot1
is placed in the /assets/
folder relatively to the path of the current page and the \fig
since it's given a path that doesn't start with /
or ./
will also look in that folder to try to find a figure which starts with the name sinc
. See also more about paths.
Note: If you wish to use Plots.jl
and deploy to GitHub pages, you will need to modify the .github/workflows/Deploy.yml
by adding env: GKSwstype: "100"
before the - name: Build and Deploy
line. Here is an example.
You can use !
and >
to indicate respectively a code that should be run automatically
and the output appended immediately after, or the same but with a REPL-style display:
```!
x = 5
y = x^2
```
for instance gives:
x = 5
y = x^2
In a similar way:
```>
x = 5
y = x^2
```
gives
x = 5
y = x^2
Shell, Pkg, Help, these modes are also experimentally supported:
Pkg mode :
```]
st
```
gives
st
Shell mode : (note: in a multi-line setting, each line is assumed to be a separate command)
```;
echo "hello!"
date
```
gives
echo "hello!"
date
<style>
.julia-help {
background-color: #fffee0;
padding: 10px;
font-style: italic;
}
.julia-help h1,h2,h3 {
font-size: 1em;
font-weight: 500;
}
</style>
Help mode : (note: only single line cell blocks will work properly)
```?
im
```
im
Note: for the help
mode above, the output is HTML corresponding to the julia
docs, it's wrapped in a julia-help
div which you should style, the above style
for instance corresponds to the following CSS:
.julia-help {
background-color: #fffee0;
padding: 10px;
font-style: italic;
}
.julia-help h1,h2,h3 {
font-size: 1em;
font-weight: 500;
}
A few things can go wrong when attempting to use and evaluate code blocks. The first thing to do when no output is shown or when an error appears is to make sure that:
@@tlist
- if the code uses packages, these packages are available in the local environment,
- the code "just works" in the REPL. @@
If this is the case and you still have issues, then you may want to force a re-evaluation of the code on the page.
In such a case, try adding @def reeval = true
on the page which will cause all code blocks on the page to be completely re-evaluated and their output re-generated.
Assuming that helped, you will then want to remove that line as otherwise that page will be fully re-evaluated every single time the page is modified which will cause unnecessary overhead.
Important note: unless you explicitly use @def reeval = true
, code blocks are evaluated only if:
@@tlist
- an earlier code block has been evaluated (in which case, since their results may depend on it, all subsequent blocks are re-evaluated),
- the content of the code block has changed.
@@
An example where this can be a bit tricky is if your code block calls a function on a file, for instance
read(file, String)
; if the underlying file is changed, the code block will not be re-evaluated (since the code doesn't change), so in such cases, you will want to use a@def reeval = true
.
The philosophy here is:
@@tlist
- keep your code snippets in appropriate subfolders of
/assets/
where they can be run and their output can be saved, this can be compared to atest/
folder in a Julia package, - run some or all of the snippets (before running Franklin),
- use
\input{...}{...}
in your markdown (see below) and when the website is updated, it will plug in the most recent parts that have been generated. @@
That way, if you modify the code, everything will be updated on the website too while ensuring that the code actually runs and generates the output you're displaying.
Again, the script files can contain # hide
at the end of lines you do not want to show (#
to be replaced by whatever symbol indicates comments in that language).
The generate_results.jl
file should run the scripts and redirect outputs to the assets/[path]/output
directory.
You can use something like the script below (if you generate an example website with newsite
, it's already in there) though you can of course modify it as you wish.
dir = @__DIR__
"""
genplain(s)
Small helper function to run some code and redirect the output (stdout) to a file.
"""
function genplain(s::String)
open(joinpath(dir, "output", "$(splitext(s)[1]).out"), "w") do outf
redirect_stdout(outf) do
include(joinpath(dir, s))
end
end
end
# run `script1.jl` and redirect what it prints to `output/script1.out`
genplain("script1.jl")
# run `script2.jl` which has a savefig(joinpath(@__DIR__, "output", "script2.png"))
include("script2.jl")
The function genplain("scriptname.jl")
just redirects the output of the script to output/scriptname.out
.
So for instance if you have in assets/scripts/script1.jl
print("hello")
Then genplain("script1.jl")
will generate /assets/scripts/output/script1.out
with content
hello
\note{You could have scripts in any language here (R
, Python
, ...) as long as the folder structure is the same.}
In order to insert the code of a script and have it highlighted you can use
\input{julia}{scripts/script1.jl}
This will insert the content of the file /assets/scripts/script1.jl
(see also the section earlier on paths) into a block that will be highlighted as julia code.
In order to insert the plain-text output of a script, you can use
\output{scripts/script1.jl}
This will insert the content of the file /assets/scripts/script1.out
into a non-highlighted code block.
In order to insert a plot generated by a script, you can use \fig
as indicated earlier or
\input{plot}{scripts/script1.jl}
or \input{plot:id}{scripts/script1.jl}
. This will look for an image file with root name /assets/scripts/script1.ext
where ext
is gif, png, jp(e)g, svg
.
If you use plot:id
then it will look for an image file with root name /assets/scripts/script1id.ext
.
The plot:id
option is useful if you have a script that generates several plots for instance.
The structure in the generate_results.jl
effectively means that all your code is run as one big script.
This also means that if you want to slice some of your code into several parts and show intermediate outputs (e.g. plots), you can just do that by having a script_1_p1.jl
, script_1_p2.jl
etc. and then just use \input
multiple times.