rstudio / learnr

Interactive Tutorials with R Markdown
https://pkgs.rstudio.com/learnr
Apache License 2.0
713 stars 240 forks source link

Write all hints in chunks #632

Open gadenbuie opened 2 years ago

gadenbuie commented 2 years ago

Now that we have a data structure for describing the interactive exercises, we're still missing hints. The primary problem is that they can be added via supporting R chunks or by using a <div> with an id that ends in -hint.

I'd like to restructure the hints so that they are always written in chunks in a tutorial. I'd also like to build on this to have the pop-up display all hints, not just code hints. We'd support any other language via knitr chunk engine, or we could use a chunk option on markdown, md, or html chunks — exercise.hint.render = TRUE — that would render the markdown or display the literal HTML in the pop-up.

```{r random, exercise = TRUE}
Think of a function that generates a _random number_.
runif()
rnorm()
gadenbuie commented 2 years ago

The breaking change would be that we'd drop support for <div> styled hints

schloerke commented 2 years ago

I like this change, that all exercise related material is chunk defined.

If we are going this route, can we add ID-solution chunk support? (This would separate the last hint from the solution)

rpruim commented 2 years ago

Here are two possible downsides to the change:

  1. It could break a lot of existing tutorials.

    That's a short term effect, but it could cause some pain in the transition.

  2. What about a hint that includes some text AND a code chunk? Will that work in the new chunk-based approach?

Regarding the second point, here is a little example based on a larger example in a learnr tutorial I was editing just today:

<div id = "residual-hint">
Residuals are calculated by subtracting the region mean from the observed per capita
GDP for each country....

Here are residuals for two of the countries.  

```{r, echo = FALSE}
set.seed(123)
means <- mean( imf_estimate ~ region, data = GDP |> filter(region != "World"), na.rm = TRUE)
# more code omitted
\```

</div>

Here's how it looks in the hint:

image

gadenbuie commented 2 years ago

Thanks @rpruim, it's helpful to have your example in thinking about this. Theoretically, I'd like the interface to support that kind of hint with syntax like this:

````{markdown residual-hint}
Residuals are calculated by subtracting the region mean from the observed per capita
GDP for each country....

Here are residuals for two of the countries.  

```{r, echo = FALSE}
set.seed(123)
means <- mean( imf_estimate ~ region, data = GDP |> filter(region != "World"), na.rm = TRUE)
# more code omitted

(Or maybe we'd need to call this engine rmd.) We'd treat the content as a small "child" doc that would be knit to html and then stored as the hint text. This way the R chunk inside the hint would be evaluated (but in a child of the knitting env, meaning that you could use objects created before this chunk but not affect subsequent chunks from these R chunks).

Thankfully, while the change would be breaking, it's a small adjustment that would open up a pathway toward an improved hint experience.

gadenbuie commented 2 years ago

Actually, we might want to use the new verbatim or asis knitr engines from knitr 1.37: https://github.com/yihui/knitr/releases/tag/v1.37

rpruim commented 2 years ago

If that sort of nesting can be supported in the new system, then I think the temporary pain of breaking changes is probably worth it.

rpruim commented 2 years ago

Those new knitr features look handy.