Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/ginolhac/unilur
A quarto extension for writing teaching practicals
https://github.com/ginolhac/unilur
quarto-extension
Last synced: 11 days ago
JSON representation
A quarto extension for writing teaching practicals
- Host: GitHub
- URL: https://github.com/ginolhac/unilur
- Owner: ginolhac
- License: mit
- Created: 2022-09-07T09:32:25.000Z (about 2 years ago)
- Default Branch: main
- Last Pushed: 2024-09-13T11:48:53.000Z (about 2 months ago)
- Last Synced: 2024-09-14T01:34:43.024Z (about 2 months ago)
- Topics: quarto-extension
- Language: Lua
- Homepage: https://ginolhac.github.io/unilur/example-solution.html
- Size: 1.53 MB
- Stars: 7
- Watchers: 2
- Forks: 2
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Changelog: NEWS.md
- License: LICENSE.md
Awesome Lists containing this project
README
unilur
================## Aim
Convert [{unilur}](https://github.com/koncina/unilur) developed by
[**Eric Koncina**](https://github.com/koncina) to a
[quarto-ext](https://github.com/quarto-ext). The current package does
more than the hiding/highlighting of **solution** code chunks for
teaching practicals.## Installation
**Important**: this extension requires Quarto **>= 1.4.538** (current release: **1.4.555**)
``` bash
quarto add ginolhac/unilur
```More details are available in Quarto docs for [managing
extensions](https://quarto.org/docs/extensions/#managing-extensions).## Usage
- **Activate** the extension by adding the following lines to your YAML
header (you can use only HTML or PDF outputs):``` yaml
format:
unilur-html: default
unilur-html+solution:
# You have to specify a different output file otherwise they will
# overwrite themselves
output-file: example-solution.html
unilur-pdf: default
unilur-pdf+solution:
output-file: example-solution.pdf
```- **Solution** code blocks are either **highlighted** or ~~discarded~~
according to the `show-solution` Boolean and the YAML header:``` yaml
show-solution: true # or false
```### Code chunks
- Add the new variable `unilur-solution` as [hashpipe,
`#|`](https://quarto.org/docs/reference/cells/cells-knitr.html) to the
code chunks that are part of practical answers. Otherwise, chunks are
left untouched.#| unilur-solution: true
Of note, if `show-solution` is absent, it is considered `false`.
Solution blocks are collapsed by default but can be shown with the chunk option `unilur-collapse` (see example below).
### Fenced div
::: unilur-solution
this is not shown in instructions
:::
Fences divs can also be used, and they can contain code chunks too### Code Blocks
The third possibility is to use `block` chunks with the appropriate chunk option
```{block}
#| unilur-solution: true
this is not shown in instructions
```## Outputs
From the Quarto doc [`example.qmd`](https://github.com/ginolhac/unilur/blob/main/example.qmd) after running:
``` bash
quarto render example.qmd
```**Both HTML** files are rendered along with **both PDFs**
| HTML `show-solution: true` (rendered: [`example-solution.html`](https://ginolhac.github.io/unilur/example-solution.html)) | HTML `show-solution: false` (rendered: [`example.html`](https://ginolhac.github.io/unilur/example.html)) |
|---------------------------------------------|-------------------------------------------------|
| ![HTML unilur-solution](img/unilur_solution.png) | ![HTML unilur-nosolution](img/unilur_nosolution.png) || PDF `show-solution: true` (rendered: [`example-solution.pdf`](https://ginolhac.github.io/unilur/example-solution.pdf)) | PDF `show-solution: false` (rendered: [`example.pdf`](https://ginolhac.github.io/unilur/example.pdf)) |
|---------------------------------------------|-------------------------------------------------|
| ![PDF unilur-solution](img/pdf_unilur_solution.png) | ![PDF unilur-nosolution](img/pdf_unilur_nosolution.png) |## Acknowledgements
People who developed and released extensions I got inspiration from:
- [JJ Allaire](https://github.com/jjallaire) for
[`filename`](https://github.com/quarto-ext/code-filename) extension
- [Andrie de Vries](https://github.com/andrie) for
[`reveal-auto-agenda`](https://github.com/andrie/reveal-auto-agenda)
extensionMoreover:
- [Christophe Dervieux](https://github.com/cderv) for precious advises and his precious time
- [Mickaël Canouil](https://github.com/mcanouil/) for maintaining the
[awesome Quarto](https://github.com/mcanouil/awesome-quarto)### TODO
- Add tests.
- callout-tip in yellow for PDF output
- Remove the `unilur-solution: true` option from chunks when displayed (visible in `echo: fenced`).
+ Idea from Christophe Dervieux: treat `div` attribute
+ Get `el` content and iterate with `walk()`
+ Add a filter `CodeBlock` which remove the `unilur` options### Debugging
This helped me developing this extension.
### Get the `Pandoc` structure after the `lua` filter
Described [here in the docs](https://quarto.org/docs/extensions/lua.html#native-format)
``` yaml
format: native
```Which returns something like this:
pandoc AST
```
Pandoc
Meta
{ unMeta =
fromList
[ ( "biblio-config" , MetaBool True )
, ( "labels"
, MetaMap
(fromList
[ ( "abstract" , MetaInlines [ Str "Abstract" ] )
, ( "affiliations"
, MetaInlines [ Str "Affiliations" ]
)
, ( "authors" , MetaInlines [ Str "Authors" ] )
, ( "description"
, MetaInlines [ Str "Description" ]
)
, ( "doi" , MetaInlines [ Str "Doi" ] )
, ( "modified" , MetaInlines [ Str "Modified" ] )
, ( "published" , MetaInlines [ Str "Published" ] )
])
)
, ( "solution" , MetaBool True )
, ( "title"
, MetaInlines [ Str "Unilur" , Space , Str "Example" ]
)
]
}
[ Header 2 ( "usage" , [] , [] ) [ Str "Usage" ]
, BulletList
[ [ Plain
[ Strong [ Str "Activate" ]
, Space
, Str "the"
, Space
, Str "extension"
, Space
, Str "by"
, Space
, Str "adding"
, Space
, Str "the"
, Space
, Str "following"
, Space
, Str "lines"
, Space
, Str "to"
, Space
, Str "your"
, Space
, Str "YAML"
, Space
, Str "header:"
]
]
]
, CodeBlock
( "" , [ "yaml" ] , [] ) "filters:\n - unilur\n"
, BulletList
[ [ Plain
[ Strong [ Str "Solution" ]
, Space
, Str "code"
, Space
, Str "blocks"
, Space
, Str "are"
, Space
[...]
, BulletList
[ [ Plain
[ Str "Solution"
, Space
, Str "with"
, Space
, Code ( "" , [] , [] ) "collapse: true"
]
]
]
, Div
( "" , [ "cell" ] , [ ( "solution" , "true" ) ] )
[ CodeBlock
( "" , [ "r" , "cell-code" ] , [] ) "1 + 2\n## [1] 3"
]
]
```### To get the intermediate markdown document
quarto render example.qmd -M keep-md:true
/usr/lib/rstudio/resources/app/bin/quarto/bin/tools/pandoc -f markdown -t json -o ast.json example.md
Then visualize the Abstract Syntax Tree (AST) using [this
code](https://bookdown.org/yihui/rmarkdown-cookbook/lua-filters.html)pandoc AST
``` r
xfun:::tree(
jsonlite::fromJSON('ast.json', simplifyVector = FALSE)
)
`````` markdown
List of 3
|-pandoc-api-version:List of 3
| |-: int 1
| |-: int 22
| |-: int 2
|-meta :List of 5
| |-execute:List of 2
| | |-t: chr "MetaMap"
| | |-c:List of 1
| | |-keep-md:List of 2
| | |-t: chr "MetaBool"
| | |-c: logi TRUE
| |-filters:List of 2
| | |-t: chr "MetaList"
| | |-c:List of 1
| | |-:List of 2
| | |-t: chr "MetaInlines"
| | |-c:List of 1
| | |-:List of 2
| | |-t: chr "Str"
| | |-c: chr "unilur"
| |-format :List of 2
| | |-t: chr "MetaMap"
| | |-c:List of 1
| | |-html:List of 2
| | |-t: chr "MetaMap"
| | |-c:List of 1
| | |-theme:List of 2
| | |-t: chr "MetaInlines"
| | |-c:List of 1
| | |-:List of 2
| | |-t: chr "Str"
| | |-c: chr "cosmo"
| |-title :List of 2
| | |-t: chr "MetaInlines"
| | |-c:List of 3
| | |-:List of 2
| | | |-t: chr "Str"
| | | |-c: chr "Unilur"
| | |-:List of 1
| | | |-t: chr "Space"
| | |-:List of 2
| | |-t: chr "Str"
| | |-c: chr "Example"
| |-unilur :List of 2
| |-t: chr "MetaMap"
| |-c:List of 1
| |-solution:List of 2
| |-t: chr "MetaInlines"
| |-c:List of 1
| |-:List of 2
| |-t: chr "Str"
| |-c: chr "true"
|-blocks :List of 5
|-:List of 2
| |-t: chr "Div"
| |-c:List of 2
| |-:List of 3
| | |-: chr ""
| | |-:List of 1
| | | |-: chr "cell"
| | |-:List of 1
| | |-:List of 2
| | |-: chr "solution"
| | |-: chr "true"
| |-:List of 2
| |-:List of 2
| | |-t: chr "CodeBlock"
| | |-c:List of 2
| | |-:List of 3
| | | |-: chr ""
| | | |-:List of 1
| | | | |-: chr "cell-code"
| | | |-: list()
| | |-: chr "```{r}\n#| solution: true\n\n1 + 1\n```"
| |-:List of 2
| |-t: chr "Div"
| |-c:List of 2
| |-:List of 3
| | |-: chr ""
| | |-:List of 2
| | | |-: chr "cell-output"
| | | |-: chr "cell-output-stdout"
| | |-: list()
| |-:List of 1
| |-:List of 2
| |-t: chr "CodeBlock"
| |-c:List of 2
| |-:List of 3
| | |-: chr ""
| | |-: list()
| | |-: list()
| |-: chr "[1] 2"
|-:List of 2
| |-t: chr "Para"
| |-c:List of 1
| |-:List of 2
| |-t: chr "Str"
| |-c: chr "Classic"
|-:List of 2
| |-t: chr "Div"
| |-c:List of 2
| |-:List of 3
| | |-: chr ""
| | |-:List of 1
| | | |-: chr "cell"
| | |-: list()
| |-:List of 2
| |-:List of 2
| | |-t: chr "CodeBlock"
| | |-c:List of 2
| | |-:List of 3
| | | |-: chr ""
| | | |-:List of 1
| | | | |-: chr "cell-code"
| | | |-: list()
| | |-: chr "```{r}\n1 + 1\n```"
| |-:List of 2
| |-t: chr "Div"
| |-c:List of 2
| |-:List of 3
| | |-: chr ""
| | |-:List of 2
| | | |-: chr "cell-output"
| | | |-: chr "cell-output-stdout"
| | |-: list()
| |-:List of 1
| |-:List of 2
| |-t: chr "CodeBlock"
| |-c:List of 2
| |-:List of 3
| | |-: chr ""
| | |-: list()
| | |-: list()
| |-: chr "[1] 2"
|-:List of 2
| |-t: chr "Para"
| |-c:List of 1
| |-:List of 2
| |-t: chr "Str"
| |-c: chr "Solution"
|-:List of 2
|-t: chr "Div"
|-c:List of 2
|-:List of 3
| |-: chr ""
| |-:List of 1
| | |-: chr "cell"
| |-:List of 1
| |-:List of 2
| |-: chr "solution"
| |-: chr "true"
|-:List of 2
|-:List of 2
| |-t: chr "CodeBlock"
| |-c:List of 2
| |-:List of 3
| | |-: chr ""
| | |-:List of 2
| | | |-: chr "r"
| | | |-: chr "cell-code"
| | |-: list()
| |-: chr "1 + 2"
|-:List of 2
|-t: chr "Div"
|-c:List of 2
|-:List of 3
| |-: chr ""
| |-:List of 2
| | |-: chr "cell-output"
| | |-: chr "cell-output-stdout"
| |-: list()
|-:List of 1
|-:List of 2
|-t: chr "CodeBlock"
|-c:List of 2
|-:List of 3
| |-: chr ""
| |-: list()
| |-: list()
|-: chr "[1] 3"
```## License
MIT License