@def hascode = true # Inserting and evaluating code \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 ## Overview ### Inserting code 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: `````plaintext This is some `inline code` or ``inline ` code with a tick``. ````` * **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. ### Evaluating code When presenting code in a post, it's often convenient to have a way to check the code works and the output shown corresponds to the code. In Franklin there are two approaches that help you for this: @@tlist 1. For Julia code, a **live-evaluation** of code blocks is supported, 1. 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. @@ ## Live evaluation (Julia) Julia code blocks can be evaluated on the fly and their output 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 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 1. write the code to the file `/assets/[subpath]/code/ex1.jl` 1. 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_ than to 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 ``` ### More on paths There are three ways you can specify where the script corresponding to a code-block should be saved. @@tlist 1. _relative to the page_: `./[p]/script` is as above, it will write the code block to `/assets/[subpath]/p/script.jl` where `subpath` corresponds to the sub-path of the page where the code block is inserted (path below `/src/`) 1. _relative to the assets dir_: `p/script` will write the code block to `/assets/p/script.jl` 1. _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. ### Inserting the output 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 ```html
dot(a, a) = 14
``` 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: ```julia 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: ```julia:ex_show x = 5 println("hello") x^2 ``` \show{ex_show} ### Hiding lines 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**. ### Project.toml 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 ```julia-repl (1.x) pkg> activate . (myWebsite) pkg> add PyCall ``` once that's done, if you now start the server, Franklin will write ```julia-repl 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 [MLJ Tutorials](https://alan-turing-institute.github.io/MLJTutorials/)). ### Plots 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 `````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: ```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} **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](#more_on_paths). ### Troubleshooting A few things can go wrong when attempting to use and evaluate code blocks. The first thing to do if no output is shown or an error appears is to make sure that: @@tlist 1. if the code uses packages, these packages are available in the local environment, 1. the code "just works" in the REPL. @@ If this is the case and you still have issues, then you may want to force 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 an 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`. ## Offline evaluation (any language) 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 a `test/` 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. ```julia 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` ```julia print("hello") ``` Then `genplain("script1.jl")` will generate `/assets/scripts/output/script1.out` with content ```julia hello ``` \note{You could have scripts in any language here (`R`, `Python`, ...) as long as the folder structure is the same.} ### Inserting code 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. ### Plain-text output 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. ### Plot output 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. ### Slicing up 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 in 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.