Create a GitHub issue from a reprex with shrtcts

Turn a reprex into a GitHub issue using a custom RStudio addin with shrtcts.

R
Addin
Shortcuts
GitHub
reprex
Author

Garrick Aden-Buie

Published

September 3, 2021

Keywords

rstats

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

library(tidyverse)

tibble(date = "2020-01-01") %>%
  mutate(year = case_when(
    date <= "2020-12-31" & date >= "2020-01-01" ~ 2020,
    is.na(date) ~ NA
  ))

Reprex Preview

library(tidyverse)

tibble(date = "2020-01-01") %>%
  mutate(year = case_when(
    date <= "2020-12-31" & date >= "2020-01-01" ~ 2020,
    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
sessioninfo::session_info()
#> ─ 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(
    date <= "2020-12-31" & date >= "2020-01-01" ~ 2020,
    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)
```

<sup>Created on 2023-02-12 with [reprex v2.0.2](https://reprex.tidyverse.org)</sup>

<details style="margin-bottom:10px;">
<summary>
Session info
</summary>

``` r
sessioninfo::session_info()
#> ─ 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(
    date <= "2020-12-31" & date >= "2020-01-01" ~ 2020,
    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(
    date <= "2020-12-31" & date >= "2020-01-01" ~ 2020,
    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
sessioninfo::session_info()
#> ─ 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

I’m using the latest version of the `tidyverse`,
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(
    date <= "2020-12-31" & date >= "2020-01-01" ~ 2020,
    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)
```

<sup>Created on 2023-02-12 with [reprex v2.0.2](https://reprex.tidyverse.org)</sup>

<details style="margin-bottom:10px;">
<summary>
Session info
</summary>

``` r
sessioninfo::session_info()
#> ─ 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:

usethis::browse_github_issues()

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!).

input <- "runif(min = 0, max = 10)\n"
repo <- "gadenbuie/shrtcts"

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.

body <- "... reprex body goes here ..."
url_new_issue <- glue::glue("https://github.com/{repo}/issues/new?body={body}")
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()

body <- reprex::reprex(input = input, venue = "gh", html_preview = TRUE)

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…

body <- reprex::reprex(input = input, venue = "gh", html_preview = TRUE)
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.

body <- paste(body, collapse = "\n")
[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.

body <- URLencode(body, reserved = TRUE)
[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.

url_new_issue <- glue::glue("https://github.com/{repo}/issues/new?body={body}")
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.

A new GitHub issue page with our reprex as the issue body

A new GitHub issue page with our reprex as the issue body

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.

ctx <- rstudioapi::getSourceEditorContext()
selection <- ctx$selection[[1]]$text

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.

input <- if (nzchar(selection)) {
  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)) {
  ctx <- rstudioapi::getSourceEditorContext()
  selection <- ctx$selection[[1]]$text
  input <- if (nzchar(selection)) {
    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.

repo_guess <- tryCatch(
  usethis:::target_repo_spec("source", FALSE),
  error = 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.

repo <- rstudioapi::showPrompt(
  title = "Which repository?",
  message = "Where should we create the issue? (owner/repo)",
  default = repo_guess
)

RStudio menu asking to the user to manually enter a repository.

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)) {
  repo_guess <- tryCatch(
    usethis:::target_repo_spec("source", FALSE),
    error = function(err) NULL
  )

  repo <- rstudioapi::showPrompt(
    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")

remotes::install_github("gadenbuie/shrtcts")

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.

shrtcts::edit_shortcuts()
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.

create_issue_from_reprex <- function(input = NULL, repo = NULL) {
  # 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
create_issue_from_reprex <- function(input = NULL, repo = NULL) {
  # 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
create_issue_from_reprex <- function(input = NULL, repo = NULL) {
  if (is.null(repo)) {
    repo_guess <- tryCatch(
      usethis:::target_repo_spec("source", FALSE),
      error = function(err) NULL
    )
  
    repo <- rstudioapi::showPrompt(
      title = "Which repository?",
      message = "Where should we create the issue? (owner/repo)",
      default = repo_guess
    )
  }

  if (is.null(input)) {
    ctx <- rstudioapi::getSourceEditorContext()
    selection <- ctx$selection[[1]]$text
    input <- if (nzchar(selection)) {
      c(strsplit(selection, "\n")[[1]], "")
    }
  }

  body <- reprex::reprex(input = input, venue = "gh", html_preview = TRUE)
  body <- paste(body, collapse = "\n")
  body <- URLencode(body, reserved = TRUE)
  url_new_issue <- glue::glue("https://github.com/{repo}/issues/new?body={body}")
  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!

shrtcts::add_rstudio_shortcuts(set_keyboard_shortcuts = TRUE)
A demonstration of the reprex shortcut: highlight code in RStudio editor, run the 'reprex to issue' shortcut, and create a new github issue

The reprex to GitHub issue shortcut in action.