Skip to content

Commit

Permalink
feat(exercise): Support exercise.pipe option (#804)
Browse files Browse the repository at this point in the history
  • Loading branch information
gadenbuie authored Sep 27, 2023
1 parent 58aa962 commit 20e4cb6
Show file tree
Hide file tree
Showing 9 changed files with 100 additions and 10 deletions.
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

- Added a new quick restore option that restores both the last submitted exercise code and the output of that submission, if the output is available to be restored. This option is enabled by setting the global option `tutorial.quick_restore = 2` or the environment variable `TUTORIAL_QUICK_RESTORE=2`. This option augments the quick restore value when `TRUE` or `1`, wherein only the last submitted **code** is restored, such that users will need to click the "Submit" button to evaluate and see the output. (#794)

- A new `exercise.pipe` tutorial or exercise chunk option can now be used to determine which pipe operator is used for interactive exercises. The default is `"|>"` (the native R pipe) when the tutorial is rendered with R >= 4.1.0, or `"%>%"` otherwise (the magrittr pipe). You can set the pipe used for the tutorial using `tutorial_options()`, or you can use `exercise.pipe` as a knitr chunk option on an individual exercise chunk. (#804)

# learnr 0.11.4

- Moved curl from Imports to Suggests. curl is only required when using an external evaluator (#776).
Expand Down
24 changes: 18 additions & 6 deletions R/knitr-hooks.R
Original file line number Diff line number Diff line change
Expand Up @@ -374,12 +374,16 @@ tutorial_knitr_options <- function() {
completion <- as.numeric(options$exercise.completion %||% 1 > 0)
diagnostics <- as.numeric(options$exercise.diagnostics %||% 1 > 0)
startover <- as.numeric(options$exercise.startover %||% 1 > 0)
paste0('<div class="tutorial-', class,
'" data-label="', options$label,
'" data-completion="', completion,
'" data-diagnostics="', diagnostics,
'" data-startover="', startover,
'" data-lines="', lines, '">')
paste0(
'<div class="tutorial-', class,
'" data-label="', options$label,
'" data-completion="', completion,
'" data-diagnostics="', diagnostics,
'" data-startover="', startover,
'" data-lines="', lines,
'" data-pipe="', htmltools::htmlEscape(exercise_option_pipe(options)),
'">'
)
}
# after exercise
else {
Expand Down Expand Up @@ -575,3 +579,11 @@ verify_tutorial_chunk_label <- function() {
)
}
}

exercise_option_pipe <- function(options = knitr::opts_chunk$get()) {
if (!is.null(options[["exercise.pipe"]])) {
return(options[["exercise.pipe"]])
}

if (getRversion() < "4.1.0") "%>%" else "|>"
}
8 changes: 8 additions & 0 deletions R/options.R
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@
#' (defaults to \code{30}).
#' @param exercise.lines Lines of code for exercise editor (defaults to the
#' number of lines in the code chunk).
#' @param exercise.pipe The characters to enter when the user presses the
#' "Insert Pipe" keyboard shortcut in the exercise editor
#' (`Ctrl/Cmd + Shift + M`). This can be set at the tutorial level or for an
#' individual exercise. If `NULL` (default), the base R pipe (`|>`) is used
#' when the tutorial is rendered in R >= 4.1.0, otherwise the \pkg{magrittr}
#' pipe (`%>%`) is used.
#' @param exercise.blanks A regular expression to be used to identify blanks in
#' submitted code that the user should fill in. If `TRUE` (default), blanks
#' are three or more underscores in a row. If `FALSE`, blank checking is not
Expand All @@ -37,6 +43,7 @@ tutorial_options <- function(exercise.cap = NULL,
exercise.eval = FALSE,
exercise.timelimit = 30,
exercise.lines = NULL,
exercise.pipe = NULL,
exercise.blanks = NULL,
exercise.checker = NULL,
exercise.error.check.code = NULL,
Expand All @@ -53,6 +60,7 @@ tutorial_options <- function(exercise.cap = NULL,
eval(parse(text = sprintf(set_option_code, "exercise.eval")))
eval(parse(text = sprintf(set_option_code, "exercise.timelimit")))
eval(parse(text = sprintf(set_option_code, "exercise.lines")))
eval(parse(text = sprintf(set_option_code, "exercise.pipe")))
eval(parse(text = sprintf(set_option_code, "exercise.blanks")))
eval(parse(text = sprintf(set_option_code, "exercise.checker")))
eval(parse(text = sprintf(set_option_code, "exercise.error.check.code")))
Expand Down
2 changes: 1 addition & 1 deletion inst/lib/tutorial/tutorial.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions inst/lib/tutorial/tutorial.js.map

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion learnr-js/tutorial/tutorial.js
Original file line number Diff line number Diff line change
Expand Up @@ -1087,7 +1087,9 @@ Tutorial.prototype.$initializeExerciseEditors = function () {
}
})
}
bindInsertKey('insertPipe', 'Ctrl+Shift+M', { r: ' %>% ' })

const pipeCode = exercise.attr('data-pipe') || '%>%'
bindInsertKey('insertPipe', 'Ctrl+Shift+M', { r: ' ' + pipeCode })
bindInsertKey('insertArrow', 'Alt+-', { r: ' <- ', fallback: ' = ' })

// re-focus the editor on run button click
Expand Down
8 changes: 8 additions & 0 deletions man/tutorial_options.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

56 changes: 56 additions & 0 deletions tests/manual/pipe-option/pipe-option.Rmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
---
title: "Exercise Pipe Option"
output: learnr::tutorial
runtime: shiny_prerendered
---

```{r setup, include=FALSE}
library(learnr)
library(dplyr)
knitr::opts_chunk$set(echo = FALSE)
```


## Ceci n'est pas une pipe

Qu'est-ce qu'une pipe, sinon l'idée d'une pipe?

You can set the characters used for the pipe shortcut at the tutorial level with

```r
knitr::opts_chunk$set(exercise.pipe = "%>%")
```

Or you can set the pipe option at the individual level using the `exercise.pipe` chunk option.

````{verbatim echo = TRUE}
```{r base-pipe, exercise=TRUE, exercise.pipe="|>"}
mtcars count(cyl)
```
````

By default, if not set otherwise set, learnr will use the base R pipe (`|>`) when the tutorial is rendered in R >= 4.1.0.

### Old school

In this next chunk, pressing `Ctrl/Cmd + Shift + M` enters the magrittr pipe: `%>%`.

```{r magrittr, exercise=TRUE, exercise.pipe="%>%"}
mtcars count(cyl)
```

### New school

In this next chunk, pressing `Ctrl/Cmd + Shift + M` enters the base R pipe: `|>`.

```{r pipe, exercise=TRUE, exercise.pipe="|>"}
mtcars count(cyl)
```

### Night school

In this next chunk, pressing `Ctrl/Cmd + Shift + M` enters the pipe that matches your R version. It's the base R pipe for R >= 4.1.

```{r auto, exercise=TRUE}
mtcars count(cyl)
```
2 changes: 2 additions & 0 deletions vignettes/articles/exercises.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ There are many options associated with tutorial exercises (all of which are desc
+-----------------------------+-------------------------------------------------------------------------------------------------------------------------------------+
| `exercise.lines` | Lines of code for exercise editor (default to size of code chunk). |
+-----------------------------+-------------------------------------------------------------------------------------------------------------------------------------+
| `exercise.pipe` | The code to insert when the user presses `Ctrl/Cmd + Shift + M` (defaults to `|>` for R >= 4.1.0, otherwise `%>%`). |
+-----------------------------+-------------------------------------------------------------------------------------------------------------------------------------+
| `exercise.timelimit` | Number of seconds to limit execution time to (defaults to 30). |
+-----------------------------+-------------------------------------------------------------------------------------------------------------------------------------+
| `exercise.checker` | Function used to check exercise answers (e.g., `gradethis::grade_learnr()`). |
Expand Down

0 comments on commit 20e4cb6

Please sign in to comment.