Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Authentic usage of <<blah>> in R code can't be knitted #2360

Closed
jennybc opened this issue Aug 29, 2024 · 8 comments
Closed

Authentic usage of <<blah>> in R code can't be knitted #2360

jennybc opened this issue Aug 29, 2024 · 8 comments
Assignees
Labels
bug Bugs

Comments

@jennybc
Copy link
Contributor

jennybc commented Aug 29, 2024

This is distilled from a Slack conversation with @cderv where we figured out what was going on.

I'm writing a new vignette for glue about how to write a wrapper around glue::glue(). A good example is if you want to create a glue() variant that changes the delimiters, probably because the text you're working with has a legitimate use of { and }:

myglue <- function(..., .envir = parent.frame()) {
  glue(..., .open = "<<", .close = ">>", .envir = .envir)
}

<< and >> are the most common choice here, because they are so strongly associated with templating.

The problem is that knitr's own templating often captures <<blah>> and modifies/removes that, breaking the code. I'm talking about this: https://bookdown.org/yihui/rmarkdown-cookbook/reuse-chunks.html.

Consider this .Rmd document. The R code defines a string that could be used with myglue() (defined above) to produce the code for an R function definition:

---
title: "Angle bracket test"
output: html_document
---

```{r}
x <- "
  <<NAME>> <- function(x) {
    <<<BODY>>>
  }"

x
nchar(x)

Running this code interactively produces:

> x <- "
+   <<NAME>> <- function(x) {
+     <<<BODY>>>
+   }"
> x
[1] "\n  <<NAME>> <- function(x) {\n    <<<BODY>>>\n  }"
> nchar(x)
[1] 47

But knitting this document results in:

x <- "
  <<NAME>> <- function(x) {
  }"

x
#> [1] "\n  <<NAME>> <- function(x) {\n  }"
nchar(x)
#> [1] 32

The line containing <<BODY>> has gone missing! Because it matches this regex: "^\\s*<<(.+)>>\\s*$".

Putting it all together, interactive execution of some code looks like this:

> library(glue)
> myglue <- function(..., .envir = parent.frame()) {
+   glue(..., .open = "<<", .close = ">>", .envir = .envir)
+ }
> myfun <- function(NAME, BODY) {
+   x <- "
+     <<NAME>> <- function(x) {
+       <<BODY>>
+     }"
+   myglue(x, NAME = NAME, BODY = BODY)
+ }
> myfun("one_plus_one", "1 + 1")
one_plus_one <- function(x) {
  1 + 1
}

But knitting that same code produces:

library(glue)

myglue <- function(..., .envir = parent.frame()) {
  glue(..., .open = "<<", .close = ">>", .envir = .envir)
}

myfun <- function(NAME, BODY) {
  x <- "
    <<NAME>> <- function(x) {
    }"
  myglue(x, NAME = NAME, BODY = BODY)
}

myfun("one_plus_one", "1 + 1")
#> one_plus_one <- function(x) {
#> }

A few thoughts:

  • It feels like <<BODY>> in my example should be protected from knitr's templating because it occurs inside a string.
  • It would be nice if there was a way to opt out of knitr's templating or if it was even turned off by default.
  • I had some hope that I could just use <<< and >>> but that matches the regex too. There is no amount of brackets that will work.

(This is really neither here nor there but the link above says that "The marker <<label>> does not have to be on a separate line. It can be embedded anywhere in a code chunk." This seems to be not be true any more, based on the regex and from the fact that <<NAME>> is not a problem in my example, only <<BODY>>, which appears on its own line.)

@cderv cderv moved this to To discuss / To plan in R Markdown Team Projects Aug 30, 2024
@jennybc
Copy link
Contributor Author

jennybc commented Aug 30, 2024

Here's the new glue vignette where I would love to use this example function throughout (and the delimiters << and >>), but have to use to different delimiters in any chunks where eval = TRUE:

myglue <- function(..., .envir = parent.frame()) {
  glue(..., .open = "<<", .close = ">>", .envir = .envir)
}

https://glue.tidyverse.org/dev/articles/wrappers.html

Update: I changed the example while writing that vignette and the new one doesn't ever have text like <<blah>> isolated on its own line. Which means that I can get away with using << and >> (and maybe I will switch?). But I still think it would be good to have one or more of the measures suggested above.

@gadenbuie
Copy link
Contributor

gadenbuie commented Sep 14, 2024

This was also reported by an epoxy user: gadenbuie/epoxy#127

Here's another minimal reprex showing that this issue impacts other knitr engines.

---
output: github_document
---

```{verbatim}
<<x>>
  <<y>>
    <<z>> other <<a>>
```

```{verbatim}
.<<x>>
```

When the above document is rendered, lines that start with << and contain no other delimted text, or that start and end with <<...>> are removed:

``` default
```

``` default
.<<x>>
```

@yihui yihui added the bug Bugs label Sep 17, 2024
yihui added a commit to rstudio/rmarkdown-cookbook that referenced this issue Sep 17, 2024
@yihui yihui closed this as completed in 49f8793 Sep 17, 2024
@github-project-automation github-project-automation bot moved this from To discuss / To plan to Done in R Markdown Team Projects Sep 17, 2024
@yihui
Copy link
Owner

yihui commented Sep 17, 2024

I just applied a minimal fix so that knitr will ignore <<label>> if label is not found in the document, which I think should be enough to avoid the problems reported above. Of course, it will be better if users can control whether the <<label>> references should be resolved through a chunk option. Please let me know if you need this level of control. Thanks!

The documentation at https://bookdown.org/yihui/rmarkdown-cookbook/reuse-chunks.html was wrong and I just corrected. Perhaps originally (in 2013?) I wanted to make it work that way, but decided later that <<>> should be on its own line.

@gadenbuie
Copy link
Contributor

Thanks @yihui!

Of course, it will be better if users can control whether the <> references should be resolved through a chunk option. Please let me know if you need this level of control.

Personally, I would take advantage of this option if it existed to automatically disable the feature in chunks where <<label>> syntax is inherently likely (as in both my and Jenny's examples). I also like that Quarto has a philosophically similar feature for disabling shortcodes in a given chunk that I've used frequently.

@jennybc
Copy link
Contributor Author

jennybc commented Sep 18, 2024

Of course, it will be better if users can control whether the <<label>> references should be resolved through a chunk option. Please let me know if you need this level of control.

I gave a 👍 above, but want to express my strong support for making it possible to just turn this feature off. Shall I open a new issue for that?

@yihui
Copy link
Owner

yihui commented Sep 18, 2024

No need to open a new issue. I'll add a chunk option to make it possible to turn off this feature. Thanks!

@yihui
Copy link
Owner

yihui commented Sep 18, 2024

I just added the chunk option ref.chunk and if you set it to FALSE, <<>> will be left untouched. I'll be happy to make further changes if you have any suggestions, but I probably can't make ref.chunk = FALSE the default, since it would be a breaking change.

@jennybc
Copy link
Contributor Author

jennybc commented Sep 18, 2024

Thanks @yihui! That sounds really useful. I will take the next opportunity I get to take the new option out for a spin.

yihui added a commit to yihui/yihui.org that referenced this issue Oct 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Bugs
Projects
None yet
Development

No branches or pull requests

3 participants