Skip to content

Commit

Permalink
add cross-referencing; adjust options and docs
Browse files Browse the repository at this point in the history
  • Loading branch information
tavareshugo committed Sep 4, 2024
1 parent fe4e915 commit a4dd8fe
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 17 deletions.
2 changes: 1 addition & 1 deletion _extensions/courseformat/_extension.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
title: Course Page Format
author: Cambridge Informatics Training
version: 1.2.0
version: 1.2.1
contributes:
formats:
html:
Expand Down
78 changes: 67 additions & 11 deletions _extensions/courseformat/callout_exercise.lua
Original file line number Diff line number Diff line change
@@ -1,24 +1,46 @@
local number_exercises = false
local number_exercises = true
local hide_answers = false
local exercises = {} -- Table to store exercise IDs and titles
local exercise_counter = 0 -- Global counter for exercises
local seen_ids = {} -- Table to track seen IDs and check for duplicates

-- function to throw a yellow warning
function warn(message)
local yellow = "\27[33m"
local reset = "\27[0m"
io.stderr:write(yellow .. "WARN: " .. message .. reset .. "\n")
end

-- Meta function to read yml metadata
function Meta(meta)
-- Access metadata with hyphens using bracket notation
if meta["number-exercises"] and meta["number-exercises"] == true then
if meta["exercises"] and meta["exercises"]["number"] == true then
number_exercises = true
end

if meta["hide-answers"] and meta["hide-answers"] == true then
if meta["exercises"] and meta["exercises"]["hide-answers"] == true then
hide_answers = true
end
end

local exercise_counter = 0 -- Global counter for exercises

-- Div function to process exercises and answers
function Div(div)
-- Process callout-exercise divs
if div.classes:includes("callout-exercise") then
exercise_counter = exercise_counter + 1
local exercise_number = number_exercises and ("Exercise " .. exercise_counter) or "Exercise"
local exercise_number = "Exercise " .. exercise_counter -- Always generate the number

-- Store exercise ID and title if it has an ID starting with "ex-"
if div.identifier and div.identifier:match("^ex%-") then
-- Check for duplicate IDs
if seen_ids[div.identifier] then
warn("Duplicate exercise ID found: #" .. div.identifier)
end

seen_ids[div.identifier] = true
exercises[div.identifier] = exercise_number
end

-- Process the header within the exercise
if div.content[1] ~= nil and div.content[1].t == "Header" then
local title = pandoc.utils.stringify(div.content[1])
div.content:remove(1) -- Remove the original header from content
Expand All @@ -31,20 +53,22 @@ function Div(div)
collapse = false
})
else
-- If no header, use just the exercise number (if numbering is on)
local final_title = number_exercises and exercise_number or nil
return quarto.Callout({
type = "exercise",
content = { div },
title = exercise_number,
title = final_title,
icon = false,
collapse = false
})
end
end

-- Remove callout-answer divs if hide_answers is true
-- Process callout-answer divs
if div.classes:includes("callout-answer") then
if (hide_answers and div.attributes["hide"] ~= "false") or div.attributes["hide"] == "true" then
return pandoc.RawBlock('html', '<div hidden></div>')
return pandoc.RawBlock('html', '<div hidden></div>')
else
return quarto.Callout({
type = "answer",
Expand All @@ -56,6 +80,7 @@ function Div(div)
end
end

-- Process callout-hint divs
if div.classes:includes("callout-hint") then
return quarto.Callout({
type = "hint",
Expand All @@ -67,7 +92,38 @@ function Div(div)
end
end

-- Replace cross-references in Pandoc elements
function Pandoc(doc)
-- Walk through the document and replace any Str element that matches @ex-*
local function replace_xref(elem)
if elem.t == "Str" and elem.text:match("^@ex%-") then
local id = elem.text:sub(2) -- Remove the "@" prefix
local exercise_title = exercises[id]
if exercise_title then
-- Return the custom HTML link
local link_html = string.format(
'<a href="#%s"><u>%s</u></a>',
id,
exercise_title
)
return pandoc.RawInline('html', link_html)
else
-- If the ID is not found, throw a warning
warn("Undefined exercise cross-reference: @" .. id)
end
end
return elem
end

-- Apply the replacement to all inline elements
return doc:walk({
Str = replace_xref
})
end

-- Return the filter
return {
{ Meta = Meta },
{ Div = Div }
{ Div = Div },
{ Pandoc = Pandoc }
}
5 changes: 3 additions & 2 deletions _quarto.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ project:
- "!materials/*/no_render"

format: courseformat-html
number-exercises: true
hide-answers: false
exercises:
number: true
hide-answers: false

filters:
- courseformat
Expand Down
53 changes: 50 additions & 3 deletions materials/02-content_guidelines.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,9 +202,56 @@ You guessed it, you can use `.callout-hint` (the box is also collapsed by defaul

There are a few ways to customise how exercises are rendered.

- **Exercise numbering:** by default, exercises are numbered within each section. If you want to turn this off, you can edit the `_quarto.yml` file and set the option `number-exercises: false`.
- **Hide all answers:** by default answers are rendered on the site. If you want to hide all the answers (e.g. during a course) you can edit the `_quarto.yml` file and set the option `hide-answers: true`.
- **Hide individual answers:** you can choose to hide individual answers using an attribute to the answer callout: `{.callout-answer hide = true}` will hide the answer; `{.callout-answer hide = false}` will show the answer. Note that these options overide the global option set in the `_quarto.yml` detailed above.
#### Exercise numbering

By default, exercises are numbered within each section.
You can set this option from the `_quarto.yml` file, for example to turn it off:

```yml
exercises:
number: false
```
#### Hide all answers
By default answers are rendered on the site.
If you want to hide all the answers, such that they are not rendered at all (e.g. during a course) you can edit the `_quarto.yml` file and turn this on:

```yml
exercises:
hide-answers: true
```

#### Hide individual answers

You can choose to hide individual answers using an attribute to the answer callout:

- `{.callout-answer hide = true}` will hide the answer
- `{.callout-answer hide = false}` will show the answer

Note that these options overide the global option set in the `_quarto.yml` detailed above.


#### Cross-reference exercises

In the exercise callout, you can set an id with prefix `#ex-` and then cross-reference it in the text using the handle `@ex-`.
For example, this syntax::

```md
:::{.callout-exercise #ex-demo}
An exercise demo.
:::
Somewhere in the text, I want to reference @ex-demo.
```

Will render as:

> :::{.callout-exercise #ex-demo}
> An exercise demo.
> :::
>
> Somewhere in the text, I want to reference @ex-demo.


## Figures
Expand Down

0 comments on commit a4dd8fe

Please sign in to comment.