Nvim-R icon indicating copy to clipboard operation
Nvim-R copied to clipboard

Getting knitr::read_chunk to work with Nvim-R

Open tinyheero opened this issue 5 years ago • 8 comments

I've started to use the knitr::read_chunk function to separate out the logic from my Rmarkdown files (see https://yihui.name/knitr/demo/externalization/).

For instance, consider the following test.Rmd file:

---
title: "Testing the read_chunk with Nvim-R"
---

```{r setup, message = FALSE, warning = FALSE}
library("knitr")
read_chunk("test_chunk.R")
```

```{r test-chunk}
```

My test_chunk.R file then has the following contents:

# ---- test-chunk ----
dplyr::filter(mtcars, cyl == 4)

When rendering the test.Rmd file, the logic in test_chunk.R gets read but not evaluated. It only gets evaluated when there is a chunk label that matches a label in the test_chunk.R.

It doesn't seem Nvim-R is able to work with read_chunks. Specifically, it would be nice to go over the test-chunk and be able to use one of the send commands (e.g. \cc). It would then be able to read in the logic for that chunk and run it.

tinyheero avatar Sep 17 '19 09:09 tinyheero

What is the advantage of using this system instead of source() at the appropriate place?

jalvesaq avatar Sep 17 '19 12:09 jalvesaq

related:

@yihui (the author of knitr) wrote:

I think it is legitimate to simply source() the script. There is no need to read_chunk().

mschilli87 avatar Sep 17 '19 12:09 mschilli87

What is the advantage of using this system instead of source() at the appropriate place?

The advantage becomes more apparent when you have multiple logic chunks in your test_chunk.R. For instance:

# ---- test-chunk-1 ----
dplyr::filter(mtcars, cyl == 4)

# ---- test-chunk-2 ----
dplyr::filter(mtcars, cyl == 6)

# ---- test-chunk-3 ----
dplyr::filter(mtcars, cyl == 8)

These can then be evaluated at different times in your Rmarkdown. For instance:

---
title: "Testing the read_chunk with Nvim-R"
---

```{r setup, message = FALSE, warning = FALSE}
library("knitr")
read_chunk("test_chunk.R")
```

```{r test-chunk-1}
```

Some description of the results from test-chunk-1

```{r test-chunk-2}
```

Some description of the results from test-chunk-2

```{r test-chunk-3}
```

Some description of the results from test-chunk-3

It's helpful when you have long documents and don't want too much code to be in the document itself. You could put each of those chunk logics in a separate file and then source the file in the chunk when you need it, but IMO that becomes confusing to track all the files.

tinyheero avatar Sep 17 '19 12:09 tinyheero

related:

@yihui (the author of knitr) wrote:

I think it is legitimate to simply source() the script. There is no need to read_chunk().

If I understand correctly, I think that quote might be taken out of context. Yihui was responding to the commenter's question about whether it was legitimate to use source rather than read_chunk in their situation. He was saying it was in that scenario. He wasn't suggesting that read_chunk has no use.

tinyheero avatar Sep 17 '19 12:09 tinyheero

He was saying it was in that scenario. He wasn't suggesting that read_chunk has no use.

That's right.

yihui avatar Sep 17 '19 13:09 yihui

So, a algorithm to implement the requested feature would be:

  1. The user types <LocalLeader>cc when the cursor is over a chunk.
  2. If the chunk is empty, Nvim-R gets its label and asks R to execute the corresponding code from knitr memory.

Is that correct? Is there a knitr command that gets a label as input and either returns the code to be run or directly runs the code?

jalvesaq avatar Sep 17 '19 19:09 jalvesaq

@jalvesaq:

Is there a knitr command that gets a label as input and either returns the code to be run or directly runs the code?

Seem like knitr:::knit_code$get() is what you are looking for:

#' The code manager to manage code in all chunks
#'
#' This object provides methods to manage code (as character vectors) in all
#' chunks in \pkg{knitr} source documents. For example,
#' \code{knitr::knit_code$get()} returns a named list of all code chunks (the
#' names are chunk labels), and \code{knitr::knit_code$get('foo')} returns the
#' character vector of the code in the chunk with the label \code{foo}.
#' @note The methods on this object include the \code{set()} method (i.e., you
#'   could do something like \code{knitr::knit_code$set(foo = "'my precious new
#'   code'")}), but we recommend that you do not use this method to modify the
#'   content of code chunks, unless you are
#'   \href{https://emitanaka.rbind.io/post/knitr-knitr-code/}{as creative as Emi
#'   Tanaka} and know what you are doing.

(see https://github.com/yihui/knitr/blob/7660d599c32168921ab5a89187d3636cad0e418d/R/parser.R#L44-L56)

mschilli87 avatar Sep 18 '19 06:09 mschilli87

So, a algorithm to implement the requested feature would be:

  1. The user types <LocalLeader>cc when the cursor is over a chunk.
  2. If the chunk is empty, Nvim-R gets its label and asks R to execute the corresponding code from knitr memory.

Is that correct?

That would be a sensible implementation of it.

tinyheero avatar Sep 26 '19 09:09 tinyheero