library(tidyverse)
tibble(date = "2020-01-01") %>%
mutate(year = case_when(
<= "2020-12-31" & date >= "2020-01-01" ~ 2020,
date is.na(date) ~ NA
))
Have you ever spent a few minutes hours turning a bug in your code into a reprex – a minimal reproducible example?
Getting to a reprex is 90% of the challenge. Most of the time, I find my mistake in the journey to a reprex. But sometimes, I find a legitimate bug and in those cases, I want to quickly turn my reprex into a GitHub issue.
Here’s a quick way to get there using an RStudio addin and shrtcts.
reprex
is awesome
The reprex package is awesome. If you’ve never used it before, I highly recommend that you stop reading this blog and go watch Sharla Gelfand’s make a reprex… please (or read the slides from the talk).
Your goal when making a reprex is to come up with a short bit of code that demonstrates the problem you’ve experienced and that is as self-contained as possible.
To disentangle your problem from your personal R environment, reprex takes your code, runs it in an isolated environment, and returns a rendered version of your code that’s ready to be copy-pasted into a text box on a number of common websites where R users go for help.
This last feature is one of my favorites: the rendered format of a reprex
is the perfect way to start crafting a GitHub issue. Typically, I’ll work out the reprex locally, then use the Reprex selection RStudio addin to render the code, and finally jump over to the issues tab of a GitHub repo to paste the code right there.
Here’s an example reprex from Sharla’s talk. We start with plain R code. reprex renders the R code with additional information about my session and shows me a preview. And finally it also copies the markdown I need in order to paste the reprex into a GitHub issue or other online location.
R Code
Reprex Preview
library(tidyverse)
tibble(date = "2020-01-01") %>%
mutate(year = case_when(
<= "2020-12-31" & date >= "2020-01-01" ~ 2020,
date is.na(date) ~ NA
))#> Error in `mutate()`:
#> ! Problem while computing `year = case_when(...)`.
#> Caused by error in `case_when()`:
#> Backtrace:
#> ▆
#> 1. ├─tibble(date = "2020-01-01") %>% ...
#> 2. ├─dplyr::mutate(...)
#> 3. ├─dplyr:::mutate.data.frame(...)
#> 4. │ └─dplyr:::mutate_cols(.data, dplyr_quosures(...), caller_env = caller_env())
#> 5. │ ├─base::withCallingHandlers(...)
#> 6. │ └─mask$eval_all_mutate(quo)
#> 7. └─dplyr::case_when(...)
#> 8. └─dplyr:::replace_with(...)
#> 9. └─dplyr:::check_type(val, x, name, error_call = error_call)
#> 10. └─rlang::abort(msg, call = error_call)
Created on 2023-02-12 with reprex v2.0.2
Session info
::session_info()
sessioninfo#> ─ Session info ───────────────────────────────────────────────────────────────
#> setting value
#> version R version 4.2.2 (2022-10-31)
#> os macOS Big Sur ... 10.16
#> system x86_64, darwin17.0
#> ui X11
#> language (EN)
#> collate en_US.UTF-8
#> ctype en_US.UTF-8
#> tz America/New_York
#> date 2023-02-12
#> pandoc 2.18 @ /usr/local/bin/ (via rmarkdown)
#>
#> ─ Packages ───────────────────────────────────────────────────────────────────
#> package * version date (UTC) lib source
#> assertthat 0.2.1 2019-03-21 [1] CRAN (R 4.2.0)
#> cli 3.6.0 2023-01-09 [1] CRAN (R 4.2.0)
#> colorspace 2.0-3 2022-02-21 [1] CRAN (R 4.2.0)
#> DBI 1.1.3 2022-06-18 [1] CRAN (R 4.2.0)
#> digest 0.6.31 2022-12-11 [1] CRAN (R 4.2.0)
#> dplyr * 1.0.10 2022-09-01 [1] CRAN (R 4.2.0)
#> ellipsis 0.3.2 2021-04-29 [1] CRAN (R 4.2.0)
#> evaluate 0.19 2022-12-13 [1] CRAN (R 4.2.0)
#> fansi 1.0.3 2022-03-24 [1] CRAN (R 4.2.0)
#> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.2.0)
#> forcats * 0.5.2 2022-08-19 [1] CRAN (R 4.2.0)
#> fs 1.5.2 2021-12-08 [1] CRAN (R 4.2.0)
#> generics 0.1.3 2022-07-05 [1] CRAN (R 4.2.0)
#> ggplot2 * 3.4.0 2022-11-04 [1] CRAN (R 4.2.0)
#> glue 1.6.2 2022-02-24 [1] CRAN (R 4.2.0)
#> gtable 0.3.1 2022-09-01 [1] CRAN (R 4.2.0)
#> hms 1.1.2 2022-08-19 [1] CRAN (R 4.2.0)
#> htmltools 0.5.4 2022-12-07 [1] CRAN (R 4.2.0)
#> knitr 1.42 2023-01-25 [1] CRAN (R 4.2.0)
#> lifecycle 1.0.3 2022-10-07 [1] CRAN (R 4.2.0)
#> lubridate * 1.9.0 2022-11-06 [1] CRAN (R 4.2.0)
#> magrittr 2.0.3 2022-03-30 [1] CRAN (R 4.2.0)
#> munsell 0.5.0 2018-06-12 [1] CRAN (R 4.2.0)
#> pillar 1.8.1 2022-08-19 [1] CRAN (R 4.2.1)
#> pkgconfig 2.0.3 2019-09-22 [1] CRAN (R 4.2.0)
#> purrr * 1.0.1 2023-01-10 [1] CRAN (R 4.2.0)
#> R.cache 0.15.0 2021-04-30 [1] CRAN (R 4.2.0)
#> R.methodsS3 1.8.1 2020-08-26 [1] CRAN (R 4.2.0)
#> R.oo 1.24.0 2020-08-26 [1] CRAN (R 4.2.0)
#> R.utils 2.11.0 2021-09-26 [1] CRAN (R 4.2.0)
#> R6 2.5.1 2021-08-19 [1] CRAN (R 4.2.0)
#> readr * 2.1.3 2022-10-01 [1] CRAN (R 4.2.0)
#> reprex 2.0.2 2022-08-17 [1] CRAN (R 4.2.0)
#> rlang 1.0.6 2022-09-24 [1] CRAN (R 4.2.0)
#> rmarkdown 2.20 2023-01-19 [1] CRAN (R 4.2.0)
#> scales 1.2.1 2022-08-20 [1] CRAN (R 4.2.1)
#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0)
#> stringi 1.7.12 2023-01-11 [1] CRAN (R 4.2.0)
#> stringr * 1.5.0 2022-12-02 [1] CRAN (R 4.2.0)
#> styler 1.7.0 2022-03-13 [1] CRAN (R 4.2.0)
#> tibble * 3.1.8 2022-07-22 [1] CRAN (R 4.2.0)
#> tidyr * 1.2.1 2022-09-08 [1] CRAN (R 4.2.0)
#> tidyselect 1.2.0 2022-10-10 [1] CRAN (R 4.2.0)
#> tidyverse * 1.3.2.9000 2023-01-28 [1] Github (tidyverse/tidyverse@53199b7)
#> timechange * 0.1.1 2022-11-04 [1] CRAN (R 4.2.0)
#> tzdb 0.3.0 2022-03-28 [1] CRAN (R 4.2.0)
#> utf8 1.2.2 2021-07-24 [1] CRAN (R 4.2.0)
#> vctrs 0.5.1 2022-11-16 [1] CRAN (R 4.2.0)
#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.2.0)
#> xfun 0.36 2022-12-21 [1] CRAN (R 4.2.0)
#> yaml 2.3.6 2022-10-18 [1] CRAN (R 4.2.0)
#>
#> [1] /Users/garrick/Library/R/x86_64/4.2/library
#> [2] /Library/Frameworks/R.framework/Versions/4.2/Resources/library
#>
#> ──────────────────────────────────────────────────────────────────────────────
Clipboard
``` r
library(tidyverse)
tibble(date = "2020-01-01") %>%
mutate(year = case_when(
<= "2020-12-31" & date >= "2020-01-01" ~ 2020,
date is.na(date) ~ NA
))#> Error in `mutate()`:
#> ! Problem while computing `year = case_when(...)`.
#> Caused by error in `case_when()`:
#> Backtrace:
#> ▆
#> 1. ├─tibble(date = "2020-01-01") %>% ...
#> 2. ├─dplyr::mutate(...)
#> 3. ├─dplyr:::mutate.data.frame(...)
#> 4. │ └─dplyr:::mutate_cols(.data, dplyr_quosures(...), caller_env = caller_env())
#> 5. │ ├─base::withCallingHandlers(...)
#> 6. │ └─mask$eval_all_mutate(quo)
#> 7. └─dplyr::case_when(...)
#> 8. └─dplyr:::replace_with(...)
#> 9. └─dplyr:::check_type(val, x, name, error_call = error_call)
#> 10. └─rlang::abort(msg, call = error_call)
```
[reprex v2.0.2](https://reprex.tidyverse.org)</sup>
<sup>Created on 2023-02-12 with
<details style="margin-bottom:10px;">
<summary>
Session info
</summary>
``` r
::session_info()
sessioninfo#> ─ Session info ───────────────────────────────────────────────────────────────
#> setting value
#> version R version 4.2.2 (2022-10-31)
#> os macOS Big Sur ... 10.16
#> system x86_64, darwin17.0
#> ui X11
#> language (EN)
#> collate en_US.UTF-8
#> ctype en_US.UTF-8
#> tz America/New_York
#> date 2023-02-12
#> pandoc 2.18 @ /usr/local/bin/ (via rmarkdown)
#>
#> ─ Packages ───────────────────────────────────────────────────────────────────
#> package * version date (UTC) lib source
#> assertthat 0.2.1 2019-03-21 [1] CRAN (R 4.2.0)
#> cli 3.6.0 2023-01-09 [1] CRAN (R 4.2.0)
#> colorspace 2.0-3 2022-02-21 [1] CRAN (R 4.2.0)
#> DBI 1.1.3 2022-06-18 [1] CRAN (R 4.2.0)
#> digest 0.6.31 2022-12-11 [1] CRAN (R 4.2.0)
#> dplyr * 1.0.10 2022-09-01 [1] CRAN (R 4.2.0)
#> ellipsis 0.3.2 2021-04-29 [1] CRAN (R 4.2.0)
#> evaluate 0.19 2022-12-13 [1] CRAN (R 4.2.0)
#> fansi 1.0.3 2022-03-24 [1] CRAN (R 4.2.0)
#> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.2.0)
#> forcats * 0.5.2 2022-08-19 [1] CRAN (R 4.2.0)
#> fs 1.5.2 2021-12-08 [1] CRAN (R 4.2.0)
#> generics 0.1.3 2022-07-05 [1] CRAN (R 4.2.0)
#> ggplot2 * 3.4.0 2022-11-04 [1] CRAN (R 4.2.0)
#> glue 1.6.2 2022-02-24 [1] CRAN (R 4.2.0)
#> gtable 0.3.1 2022-09-01 [1] CRAN (R 4.2.0)
#> hms 1.1.2 2022-08-19 [1] CRAN (R 4.2.0)
#> htmltools 0.5.4 2022-12-07 [1] CRAN (R 4.2.0)
#> knitr 1.42 2023-01-25 [1] CRAN (R 4.2.0)
#> lifecycle 1.0.3 2022-10-07 [1] CRAN (R 4.2.0)
#> lubridate * 1.9.0 2022-11-06 [1] CRAN (R 4.2.0)
#> magrittr 2.0.3 2022-03-30 [1] CRAN (R 4.2.0)
#> munsell 0.5.0 2018-06-12 [1] CRAN (R 4.2.0)
#> pillar 1.8.1 2022-08-19 [1] CRAN (R 4.2.1)
#> pkgconfig 2.0.3 2019-09-22 [1] CRAN (R 4.2.0)
#> purrr * 1.0.1 2023-01-10 [1] CRAN (R 4.2.0)
#> R.cache 0.15.0 2021-04-30 [1] CRAN (R 4.2.0)
#> R.methodsS3 1.8.1 2020-08-26 [1] CRAN (R 4.2.0)
#> R.oo 1.24.0 2020-08-26 [1] CRAN (R 4.2.0)
#> R.utils 2.11.0 2021-09-26 [1] CRAN (R 4.2.0)
#> R6 2.5.1 2021-08-19 [1] CRAN (R 4.2.0)
#> readr * 2.1.3 2022-10-01 [1] CRAN (R 4.2.0)
#> reprex 2.0.2 2022-08-17 [1] CRAN (R 4.2.0)
#> rlang 1.0.6 2022-09-24 [1] CRAN (R 4.2.0)
#> rmarkdown 2.20 2023-01-19 [1] CRAN (R 4.2.0)
#> scales 1.2.1 2022-08-20 [1] CRAN (R 4.2.1)
#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0)
#> stringi 1.7.12 2023-01-11 [1] CRAN (R 4.2.0)
#> stringr * 1.5.0 2022-12-02 [1] CRAN (R 4.2.0)
#> styler 1.7.0 2022-03-13 [1] CRAN (R 4.2.0)
#> tibble * 3.1.8 2022-07-22 [1] CRAN (R 4.2.0)
#> tidyr * 1.2.1 2022-09-08 [1] CRAN (R 4.2.0)
#> tidyselect 1.2.0 2022-10-10 [1] CRAN (R 4.2.0)
#> tidyverse * 1.3.2.9000 2023-01-28 [1] Github (tidyverse/tidyverse@53199b7)
#> timechange * 0.1.1 2022-11-04 [1] CRAN (R 4.2.0)
#> tzdb 0.3.0 2022-03-28 [1] CRAN (R 4.2.0)
#> utf8 1.2.2 2021-07-24 [1] CRAN (R 4.2.0)
#> vctrs 0.5.1 2022-11-16 [1] CRAN (R 4.2.0)
#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.2.0)
#> xfun 0.36 2022-12-21 [1] CRAN (R 4.2.0)
#> yaml 2.3.6 2022-10-18 [1] CRAN (R 4.2.0)
#>
#> [1] /Users/garrick/Library/R/x86_64/4.2/library
#> [2] /Library/Frameworks/R.framework/Versions/4.2/Resources/library
#>
#> ──────────────────────────────────────────────────────────────────────────────
```
</details>
But wait, there’s more!
When you’re working on debugging something, going from code in your RStudio IDE to something you can share with others is huge. But reprex can do more!
Because reprex uses knitr::spin()
— knitr’s best hidden gem according to Dean Attali — to turn R code into an R Markdown document, you have a few more options.
knitr::spin()
has a cool feature that lets you write markdown in an R script. You can check out Dean Attali’s post for more details, but the gist is this: any text on a line starting with a special comment format #'
becomes markdown.
This means we can add text directly to our reprex using these comments! Below you can see that I’ve added some exposition around the problematic code.
R Code
#' I'm using the latest version of the `tidyverse`,
#' freshly installed.
library(tidyverse)
#' Suppose we have a data frame with a date column.
#' The date is stored as a _character_ vector, and
#' I'd like to convert it to a _year_ with a simple
#' comparison. The first function I thought of was
#' `case_when()`, but it doesn't seem to be doing
#' what I expect. Why am I getting this error?
tibble(date = "2020-01-01") %>%
mutate(year = case_when(
<= "2020-12-31" & date >= "2020-01-01" ~ 2020,
date is.na(date) ~ NA
))
Reprex Preview
I’m using the latest version of the tidyverse
, freshly installed.
library(tidyverse)
Suppose we have a data frame with a date column. The date is stored as a character vector, and I’d like to convert it to a year with a simple comparison. The first function I thought of was case_when()
, but it doesn’t seem to be doing what I expect. Why am I getting this error?
tibble(date = "2020-01-01") %>%
mutate(year = case_when(
<= "2020-12-31" & date >= "2020-01-01" ~ 2020,
date is.na(date) ~ NA
))#> Error in `mutate()`:
#> ! Problem while computing `year = case_when(...)`.
#> Caused by error in `case_when()`:
#> Backtrace:
#> ▆
#> 1. ├─tibble(date = "2020-01-01") %>% ...
#> 2. ├─dplyr::mutate(...)
#> 3. ├─dplyr:::mutate.data.frame(...)
#> 4. │ └─dplyr:::mutate_cols(.data, dplyr_quosures(...), caller_env = caller_env())
#> 5. │ ├─base::withCallingHandlers(...)
#> 6. │ └─mask$eval_all_mutate(quo)
#> 7. └─dplyr::case_when(...)
#> 8. └─dplyr:::replace_with(...)
#> 9. └─dplyr:::check_type(val, x, name, error_call = error_call)
#> 10. └─rlang::abort(msg, call = error_call)
Created on 2023-02-12 with reprex v2.0.2
Session info
::session_info()
sessioninfo#> ─ Session info ───────────────────────────────────────────────────────────────
#> setting value
#> version R version 4.2.2 (2022-10-31)
#> os macOS Big Sur ... 10.16
#> system x86_64, darwin17.0
#> ui X11
#> language (EN)
#> collate en_US.UTF-8
#> ctype en_US.UTF-8
#> tz America/New_York
#> date 2023-02-12
#> pandoc 2.18 @ /usr/local/bin/ (via rmarkdown)
#>
#> ─ Packages ───────────────────────────────────────────────────────────────────
#> package * version date (UTC) lib source
#> assertthat 0.2.1 2019-03-21 [1] CRAN (R 4.2.0)
#> cli 3.6.0 2023-01-09 [1] CRAN (R 4.2.0)
#> colorspace 2.0-3 2022-02-21 [1] CRAN (R 4.2.0)
#> DBI 1.1.3 2022-06-18 [1] CRAN (R 4.2.0)
#> digest 0.6.31 2022-12-11 [1] CRAN (R 4.2.0)
#> dplyr * 1.0.10 2022-09-01 [1] CRAN (R 4.2.0)
#> ellipsis 0.3.2 2021-04-29 [1] CRAN (R 4.2.0)
#> evaluate 0.19 2022-12-13 [1] CRAN (R 4.2.0)
#> fansi 1.0.3 2022-03-24 [1] CRAN (R 4.2.0)
#> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.2.0)
#> forcats * 0.5.2 2022-08-19 [1] CRAN (R 4.2.0)
#> fs 1.5.2 2021-12-08 [1] CRAN (R 4.2.0)
#> generics 0.1.3 2022-07-05 [1] CRAN (R 4.2.0)
#> ggplot2 * 3.4.0 2022-11-04 [1] CRAN (R 4.2.0)
#> glue 1.6.2 2022-02-24 [1] CRAN (R 4.2.0)
#> gtable 0.3.1 2022-09-01 [1] CRAN (R 4.2.0)
#> hms 1.1.2 2022-08-19 [1] CRAN (R 4.2.0)
#> htmltools 0.5.4 2022-12-07 [1] CRAN (R 4.2.0)
#> knitr 1.42 2023-01-25 [1] CRAN (R 4.2.0)
#> lifecycle 1.0.3 2022-10-07 [1] CRAN (R 4.2.0)
#> lubridate * 1.9.0 2022-11-06 [1] CRAN (R 4.2.0)
#> magrittr 2.0.3 2022-03-30 [1] CRAN (R 4.2.0)
#> munsell 0.5.0 2018-06-12 [1] CRAN (R 4.2.0)
#> pillar 1.8.1 2022-08-19 [1] CRAN (R 4.2.1)
#> pkgconfig 2.0.3 2019-09-22 [1] CRAN (R 4.2.0)
#> purrr * 1.0.1 2023-01-10 [1] CRAN (R 4.2.0)
#> R.cache 0.15.0 2021-04-30 [1] CRAN (R 4.2.0)
#> R.methodsS3 1.8.1 2020-08-26 [1] CRAN (R 4.2.0)
#> R.oo 1.24.0 2020-08-26 [1] CRAN (R 4.2.0)
#> R.utils 2.11.0 2021-09-26 [1] CRAN (R 4.2.0)
#> R6 2.5.1 2021-08-19 [1] CRAN (R 4.2.0)
#> readr * 2.1.3 2022-10-01 [1] CRAN (R 4.2.0)
#> reprex 2.0.2 2022-08-17 [1] CRAN (R 4.2.0)
#> rlang 1.0.6 2022-09-24 [1] CRAN (R 4.2.0)
#> rmarkdown 2.20 2023-01-19 [1] CRAN (R 4.2.0)
#> scales 1.2.1 2022-08-20 [1] CRAN (R 4.2.1)
#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0)
#> stringi 1.7.12 2023-01-11 [1] CRAN (R 4.2.0)
#> stringr * 1.5.0 2022-12-02 [1] CRAN (R 4.2.0)
#> styler 1.7.0 2022-03-13 [1] CRAN (R 4.2.0)
#> tibble * 3.1.8 2022-07-22 [1] CRAN (R 4.2.0)
#> tidyr * 1.2.1 2022-09-08 [1] CRAN (R 4.2.0)
#> tidyselect 1.2.0 2022-10-10 [1] CRAN (R 4.2.0)
#> tidyverse * 1.3.2.9000 2023-01-28 [1] Github (tidyverse/tidyverse@53199b7)
#> timechange * 0.1.1 2022-11-04 [1] CRAN (R 4.2.0)
#> tzdb 0.3.0 2022-03-28 [1] CRAN (R 4.2.0)
#> utf8 1.2.2 2021-07-24 [1] CRAN (R 4.2.0)
#> vctrs 0.5.1 2022-11-16 [1] CRAN (R 4.2.0)
#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.2.0)
#> xfun 0.36 2022-12-21 [1] CRAN (R 4.2.0)
#> yaml 2.3.6 2022-10-18 [1] CRAN (R 4.2.0)
#>
#> [1] /Users/garrick/Library/R/x86_64/4.2/library
#> [2] /Library/Frameworks/R.framework/Versions/4.2/Resources/library
#>
#> ──────────────────────────────────────────────────────────────────────────────
Clipboard
`tidyverse`,
I’m using the latest version of the
freshly installed.
``` r
library(tidyverse)
```
Suppose we have a data frame with a date column.
The date is stored as a *character* vector, and
I’d like to convert it to a *year* with a simple
comparison. The first function I thought of was`case_when()`, but it doesn’t seem to be doing
what I expect. Why am I getting this error?
``` r
tibble(date = "2020-01-01") %>%
mutate(year = case_when(
<= "2020-12-31" & date >= "2020-01-01" ~ 2020,
date is.na(date) ~ NA
))#> Error in `mutate()`:
#> ! Problem while computing `year = case_when(...)`.
#> Caused by error in `case_when()`:
#> Backtrace:
#> ▆
#> 1. ├─tibble(date = "2020-01-01") %>% ...
#> 2. ├─dplyr::mutate(...)
#> 3. ├─dplyr:::mutate.data.frame(...)
#> 4. │ └─dplyr:::mutate_cols(.data, dplyr_quosures(...), caller_env = caller_env())
#> 5. │ ├─base::withCallingHandlers(...)
#> 6. │ └─mask$eval_all_mutate(quo)
#> 7. └─dplyr::case_when(...)
#> 8. └─dplyr:::replace_with(...)
#> 9. └─dplyr:::check_type(val, x, name, error_call = error_call)
#> 10. └─rlang::abort(msg, call = error_call)
```
[reprex v2.0.2](https://reprex.tidyverse.org)</sup>
<sup>Created on 2023-02-12 with
<details style="margin-bottom:10px;">
<summary>
Session info
</summary>
``` r
::session_info()
sessioninfo#> ─ Session info ───────────────────────────────────────────────────────────────
#> setting value
#> version R version 4.2.2 (2022-10-31)
#> os macOS Big Sur ... 10.16
#> system x86_64, darwin17.0
#> ui X11
#> language (EN)
#> collate en_US.UTF-8
#> ctype en_US.UTF-8
#> tz America/New_York
#> date 2023-02-12
#> pandoc 2.18 @ /usr/local/bin/ (via rmarkdown)
#>
#> ─ Packages ───────────────────────────────────────────────────────────────────
#> package * version date (UTC) lib source
#> assertthat 0.2.1 2019-03-21 [1] CRAN (R 4.2.0)
#> cli 3.6.0 2023-01-09 [1] CRAN (R 4.2.0)
#> colorspace 2.0-3 2022-02-21 [1] CRAN (R 4.2.0)
#> DBI 1.1.3 2022-06-18 [1] CRAN (R 4.2.0)
#> digest 0.6.31 2022-12-11 [1] CRAN (R 4.2.0)
#> dplyr * 1.0.10 2022-09-01 [1] CRAN (R 4.2.0)
#> ellipsis 0.3.2 2021-04-29 [1] CRAN (R 4.2.0)
#> evaluate 0.19 2022-12-13 [1] CRAN (R 4.2.0)
#> fansi 1.0.3 2022-03-24 [1] CRAN (R 4.2.0)
#> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.2.0)
#> forcats * 0.5.2 2022-08-19 [1] CRAN (R 4.2.0)
#> fs 1.5.2 2021-12-08 [1] CRAN (R 4.2.0)
#> generics 0.1.3 2022-07-05 [1] CRAN (R 4.2.0)
#> ggplot2 * 3.4.0 2022-11-04 [1] CRAN (R 4.2.0)
#> glue 1.6.2 2022-02-24 [1] CRAN (R 4.2.0)
#> gtable 0.3.1 2022-09-01 [1] CRAN (R 4.2.0)
#> hms 1.1.2 2022-08-19 [1] CRAN (R 4.2.0)
#> htmltools 0.5.4 2022-12-07 [1] CRAN (R 4.2.0)
#> knitr 1.42 2023-01-25 [1] CRAN (R 4.2.0)
#> lifecycle 1.0.3 2022-10-07 [1] CRAN (R 4.2.0)
#> lubridate * 1.9.0 2022-11-06 [1] CRAN (R 4.2.0)
#> magrittr 2.0.3 2022-03-30 [1] CRAN (R 4.2.0)
#> munsell 0.5.0 2018-06-12 [1] CRAN (R 4.2.0)
#> pillar 1.8.1 2022-08-19 [1] CRAN (R 4.2.1)
#> pkgconfig 2.0.3 2019-09-22 [1] CRAN (R 4.2.0)
#> purrr * 1.0.1 2023-01-10 [1] CRAN (R 4.2.0)
#> R.cache 0.15.0 2021-04-30 [1] CRAN (R 4.2.0)
#> R.methodsS3 1.8.1 2020-08-26 [1] CRAN (R 4.2.0)
#> R.oo 1.24.0 2020-08-26 [1] CRAN (R 4.2.0)
#> R.utils 2.11.0 2021-09-26 [1] CRAN (R 4.2.0)
#> R6 2.5.1 2021-08-19 [1] CRAN (R 4.2.0)
#> readr * 2.1.3 2022-10-01 [1] CRAN (R 4.2.0)
#> reprex 2.0.2 2022-08-17 [1] CRAN (R 4.2.0)
#> rlang 1.0.6 2022-09-24 [1] CRAN (R 4.2.0)
#> rmarkdown 2.20 2023-01-19 [1] CRAN (R 4.2.0)
#> scales 1.2.1 2022-08-20 [1] CRAN (R 4.2.1)
#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0)
#> stringi 1.7.12 2023-01-11 [1] CRAN (R 4.2.0)
#> stringr * 1.5.0 2022-12-02 [1] CRAN (R 4.2.0)
#> styler 1.7.0 2022-03-13 [1] CRAN (R 4.2.0)
#> tibble * 3.1.8 2022-07-22 [1] CRAN (R 4.2.0)
#> tidyr * 1.2.1 2022-09-08 [1] CRAN (R 4.2.0)
#> tidyselect 1.2.0 2022-10-10 [1] CRAN (R 4.2.0)
#> tidyverse * 1.3.2.9000 2023-01-28 [1] Github (tidyverse/tidyverse@53199b7)
#> timechange * 0.1.1 2022-11-04 [1] CRAN (R 4.2.0)
#> tzdb 0.3.0 2022-03-28 [1] CRAN (R 4.2.0)
#> utf8 1.2.2 2021-07-24 [1] CRAN (R 4.2.0)
#> vctrs 0.5.1 2022-11-16 [1] CRAN (R 4.2.0)
#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.2.0)
#> xfun 0.36 2022-12-21 [1] CRAN (R 4.2.0)
#> yaml 2.3.6 2022-10-18 [1] CRAN (R 4.2.0)
#>
#> [1] /Users/garrick/Library/R/x86_64/4.2/library
#> [2] /Library/Frameworks/R.framework/Versions/4.2/Resources/library
#>
#> ──────────────────────────────────────────────────────────────────────────────
```
</details>
There should be a shortcut
Kick-starting an issue report using a reprex right from within my RStudio session is great, but there’s still the part where I have to slog out of my IDE, into a browser, and find my way to the repo where this issue should go.
Most of the time, though, I’m already in the repo. And usethis has a helpful function to get me right to the issues page:
::browse_github_issues() usethis
But there’s too much typing. I want reprex to issue, with magic 🧙 ✨.
So that’s what we’ll do! In the rest of this post, we’ll use reprex and the rstudioapi package to automatically go from code to GitHub issue. Then we’ll wrap that logic into a function and turn it into an RStudio Addin with my package, shrtcts.
From reprex to issue
Suppose we have some input
code and a target repo
. Maybe we have a classic missing argument error and we want to send the issue to gadenbuie/shrtcts (please don’t!).
<- "runif(min = 0, max = 10)\n"
input <- "gadenbuie/shrtcts" repo
Prepare the issue body
Our goal is to render the reprex into an issue body
and then we’ll put together a URL that takes us to GitHub’s new issue page for the repo
with the issue body
pre-filled when we get there.
<- "... reprex body goes here ..."
body <- glue::glue("https://github.com/{repo}/issues/new?body={body}")
url_new_issue browseURL(url_new_issue)
The trick here is that you can create a new issue by going to github.com/{owner}/{repo}/issues/new
and we’re sending along the initial body using the query string ?body={body}
.
To put together the body
, we first render the input
using reprex::reprex()
<- reprex::reprex(input = input, venue = "gh", html_preview = TRUE) body
where we’ve asked for a reprex for GitHub — venue = "gh"
— and a local HTML preview — html_preview = TRUE
. You can adjust the arguments to reprex()
to fit your needs, of course.
Unfortunately, reprex::repex()
is only half the work…
<- reprex::reprex(input = input, venue = "gh", html_preview = TRUE)
body body
[1] "``` r"
[2] "runif(min = 0, max = 10)"
[3] "#> Error in runif(min = 0, max = 10): argument \"n\" is missing, with no default"
[4] "```"
Notice that it returns a character vector with one item per line of the rendered reprex. We need to collapse it all into a single string.
<- paste(body, collapse = "\n") body
[1] "``` r\nrunif(min = 0, max = 10)\n#> Error in runif(min = 0, max = 10): argument \"n\" is missing, with no default\n```"
But this still won’t fit in a URL because it contains spaces, new lines, and other characters URLs don’t like. So we need to use the base R function URLencode()
to turn the body
text into something readable only by machines.
<- URLencode(body, reserved = TRUE) body
[1] "%60%60%60%20r%0Arunif%28min%20%3D%200%2C%20max%20%3D%2010%29%0A%23%3E%20Error%20in%20runif%28min%20%3D%200%2C%20max%20%3D%2010%29%3A%20argument%20%22n%22%20is%20missing%2C%20with%20no%20default%0A%60%60%60"
Finally, we can make our new issue URL.
<- glue::glue("https://github.com/{repo}/issues/new?body={body}")
url_new_issue url_new_issue
https://github.com/gadenbuie/shrtcts/issues/new?body=%60%60%60%20r%0Arunif%28min%20%3D%200%2C%20max%20%3D%2010%29%0A%23%3E%20Error%20in%20runif%28min%20%3D%200%2C%20max%20%3D%2010%29%3A%20argument%20%22n%22%20is%20missing%2C%20with%20no%20default%0A%60%60%60
I didn’t make the link clickable, but if you were to follow it, you’d find a brand new issue page ready for you.
Grab the input from the user
Of course, we don’t want to have to define input
manualy every time we run this function. Instead, we’d rather get the input
code from
- the current selection in RStudio
- or the clipboard if nothing is selected
To make things easy, we’ll ignore the fact that RStudio has a multiple cursors feature, and we’ll just get the first selection of code. We’ll use the getSourceEditorContext()
to get the currently open text file, then we can grab the text from the first selection in that editor window.
<- rstudioapi::getSourceEditorContext()
ctx <- ctx$selection[[1]]$text selection
If nothing is selected, selection
will be an empty string, ""
, in which case we’d prefer to leave input
as NULL
so that reprex()
will look in the clipboard for our input. We also need to make sure that input
is a character vector so that reprex()
knows that input
contains the reprex code and not a path to a file.
<- if (nzchar(selection)) {
input c(strsplit(selection, "\n")[[1]], "")
}
We’ll be wrapping this up in a function where input
might be provided by the user, so we’ll only want to check for a selection if input
is NULL
.
if (is.null(input)) {
<- rstudioapi::getSourceEditorContext()
ctx <- ctx$selection[[1]]$text
selection <- if (nzchar(selection)) {
input c(strsplit(selection, "\n")[[1]], "")
} }
Pick the repository
Often, I’ll be working in the repository where I want to create the issue. usethis does a great job guessing the repository from the information in a local copy of the repo. Rather than spending forever writing our own version, let’s just reach into usethis with :::
to call target_repo_spec()
.
The function returns the current repo in the form "owner/repo"
, but since it isn’t designed to be user-facing it throws an unusual error when called from outside a git repository. We can soften this edge by catching the error with tryCatch()
and replacing the error with a NULL
value.
<- tryCatch(
repo_guess :::target_repo_spec("source", FALSE),
usethiserror = function(err) NULL
)
Of course, maybe I’ll want to create a reprex in another repository that isn’t the one I’m currently working in. So we can follow up with a prompt asking for the repo, using our guess from usethis. The prompt is created with the showPrompt()
function from rstudioapi.
<- rstudioapi::showPrompt(
repo title = "Which repository?",
message = "Where should we create the issue? (owner/repo)",
default = repo_guess
)
Finally, the function we’re putting together will also take a repo
argument that might be provided when we call it. In that case, we’d wouldn’t need to guess or ask for a repo.
if (is.null(repo)) {
<- tryCatch(
repo_guess :::target_repo_spec("source", FALSE),
usethiserror = function(err) NULL
)
<- rstudioapi::showPrompt(
repo title = "Which repository?",
message = "Where should we create the issue? (owner/repo)",
default = repo_guess
) }
Make it a shortcut
The last step in our process is to make it easy to run this code in RStudio, ideally as an RStudio addin that we can activate from the addins menu or the command palette.
This is the exact goal of the shrtcts package: shrtcts lets you turn any function into an RStudio addin.
Set up shrtcts
If you’ve never used shrtcts before, you need to do two things to get started. First, install the package, from my R-universe or from GitHub.
R-universe
# Add my universe to your list of repos
options(repos = c(
gadenbuie = "https://gadenbuie.r-universe.dev",
getOption("repos")
))
install.package("shrtcts")
GitHub
# install.packages("remotes")
::install_github("gadenbuie/shrtcts") remotes
The next thing to do is to open a .shrtcts.R
file where we’ll add our new shortcut. This is easy to do with shrtcts::edit_shortcuts()
, which will offer to create the .shrtcts.R
file if it doesn’t exit.
::edit_shortcuts() shrtcts
Would you like to create a new shrtcts file at
'~/Library/Application Support/shrtcts/.shrtcts.R' (Yes/no/cancel) yes
Creating a shortcut function
We’ll write R functions in the .shrtcts.R
file and turn them into RStudio addins by annotating those functions with roxygen-style comments.
We start with a skeleton of a function that takes two arguments: input
and repo
, neither of which are required. Inside the function, we’ll do all the steps from above, which we’ll fill in in a second.
<- function(input = NULL, repo = NULL) {
create_issue_from_reprex # guess or ask for repo
# get current selection, if available
# render reprex
# compose new issue URL
# go to the new issue page!
}
Our next step is to turn this function into a shortcut. Using roxygen2 documentation syntax, we give the function a title and description — these will be used as the title and description of the shortcut. We can also use the @shortcut
tag to set a keyboard shortcut (if you want), and the @interactive
tag lets shortcuts know that the addin should be run interactively rather than in the background.
#' Create issue from reprex
#'
#' Creates an issue from the selected or copied reprex.
#'
#' @shortcut Cmd+Ctrl+Shift+R
#' @interactive
<- function(input = NULL, repo = NULL) {
create_issue_from_reprex # guess or ask for repo
# get current selection, if available
# render reprex
# compose new issue URL
# go to the new issue page!
}
Finally, we can replace our placeholder comments with all of the code we wrote above.
#' Create issue from reprex
#'
#' Creates an issue from the selected or copied reprex.
#'
#' @shortcut Cmd+Ctrl+Shift+R
#' @interactive
<- function(input = NULL, repo = NULL) {
create_issue_from_reprex if (is.null(repo)) {
<- tryCatch(
repo_guess :::target_repo_spec("source", FALSE),
usethiserror = function(err) NULL
)
<- rstudioapi::showPrompt(
repo title = "Which repository?",
message = "Where should we create the issue? (owner/repo)",
default = repo_guess
)
}
if (is.null(input)) {
<- rstudioapi::getSourceEditorContext()
ctx <- ctx$selection[[1]]$text
selection <- if (nzchar(selection)) {
input c(strsplit(selection, "\n")[[1]], "")
}
}
<- reprex::reprex(input = input, venue = "gh", html_preview = TRUE)
body <- paste(body, collapse = "\n")
body <- URLencode(body, reserved = TRUE)
body <- glue::glue("https://github.com/{repo}/issues/new?body={body}")
url_new_issue browseURL(url_new_issue)
invisible(url_new_issue)
}
Load your shortcuts and restart your R session to activate the addin and you’ll be ready to jump from reprex to GitHub issue in no time!
::add_rstudio_shortcuts(set_keyboard_shortcuts = TRUE) shrtcts