diff --git a/DESCRIPTION b/DESCRIPTION index e6b7f28..7e30072 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -25,9 +25,9 @@ Description: Convenience wrapper that uses the 'rmarkdown' package to License: MIT + file LICENSE URL: https://reprex.tidyverse.org, https://github.com/tidyverse/reprex BugReports: https://github.com/tidyverse/reprex/issues -Depends: +Depends: R (>= 4.1) -Imports: +Imports: callr (>= 3.6.0), cli (>= 3.2.0), clipr (>= 0.4.0), @@ -40,9 +40,10 @@ Imports: rstudioapi, utils, withr (>= 2.3.0) -Suggests: +Suggests: covr, fortunes, + gh, miniUI, rprojroot, sessioninfo, @@ -50,7 +51,7 @@ Suggests: spelling, styler (>= 1.2.0), testthat (>= 3.2.1) -VignetteBuilder: +VignetteBuilder: knitr Config/Needs/website: dplyr, tidyverse/tidytemplate Config/testthat/edition: 3 diff --git a/NAMESPACE b/NAMESPACE index 68165e1..1db115b 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -13,6 +13,7 @@ export(reprex_rescue) export(reprex_rtf) export(reprex_selection) export(reprex_slack) +export(upload_gist) import(fs) import(rlang) importFrom(glue,glue) diff --git a/NEWS.md b/NEWS.md index dc0f76c..c5b1a0d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,8 @@ # reprex (development version) +* Adds `upload_gist()` to upload reprex to GitHub gists (#481, @tanho63) +* `reprex(html_preview = FALSE)` now properly avoids opening a preview window, which flows nicer with `upload_gist()` usage. + # reprex 2.1.1 * `reprex(style = FALSE)` will never nag about installing styler (#461). @@ -59,16 +62,16 @@ Specifically, this applies to use on RStudio Server and RStudio Cloud. un-`reprex()` functions, such as `reprex_clean()`. * In this context, the file containing the (un)rendered reprex is opened so the user can manually copy its contents. - + ## Filepaths `wd` is a new argument to set the reprex working directory. As a result, the `outfile` argument is deprecated and the `input` argument has new significance. Here's how to use `input` and `wd` to control reprex filepaths: -* To reprex in the current working directory, - Previously: `reprex(outfile = NA)` - Now: `reprex(wd = ".")` +* To reprex in the current working directory, + Previously: `reprex(outfile = NA)` + Now: `reprex(wd = ".")` More generally, usage looks like `reprex(wd = "path/to/desired/wd")`. * If you really care about reprex filename (and location), write your source to `path/to/stuff.R` and call `reprex(input = "path/to/stuff.R")`. When `input` @@ -199,7 +202,7 @@ R 3.1 and R 3.2 are no longer explicitly supported or tested. Our general practi reprex has a website: . It includes a contributed article from @njtierney (#103). -reprex has moved to the [tidyverse Organization](https://github.com/tidyverse). It is installed as part of the [tidyverse meta-package](https://www.tidyverse.org) and is [suggested to those seeking help](https://www.tidyverse.org/help/). +reprex has moved to the [tidyverse Organization](https://github.com/tidyverse). It is installed as part of the [tidyverse meta-package](https://www.tidyverse.org) and is [suggested to those seeking help](https://www.tidyverse.org/help/). `reprex()` gains several arguments and many arguments can now be controlled via an option, in case a user wants their own defaults. @@ -224,21 +227,21 @@ These look like `reprex(..., arg = opt(DEFAULT), ...)` in the help file. This is * Line wrapping is preserved from source via a Pandoc option (#145 @jimhester, #175). * `venue = "gh"` now targets CommonMark as the standard for GitHub Flavored Markdown (#77). - + * `venue = "so"` has appropriate whitespace at the start. * `venue = "ds"` is a new value, corresponding to , which is the platform behind [community.rstudio.com](https://forum.posit.co/). This is currently just an alias for the default `"gh"` GitHub venue, because the formatting appears to be compatible. Adding the `"ds"` value so Discourse can be documented and to guard against the possibility that some formatting is actually unique. - + ## Other changes * The `keep.source` option is set to `TRUE` when rendering the reprex, so reprexes involving srcrefs should work (#152). - + * The "undo" functions (`reprex_invert()`, `reprex_clean()`, `reprex_rescue()`) handle `input` and `outfile` like `reprex()` does. The `outfile` argument is new (#129, #68). * The default value for knitr's `upload.fun` is now set according to the venue. It is `knitr::imgur_upload()` for all venues except `"r"`, where it is `identity` (#125). * The HTML preview should appear in the RStudio Viewer more consistently, especially on Windows (#75 @yutannihilation). - + * More rigorous use of UTF-8 encoding (#76 @yutannihilation). * Expression input handling has been refactored. As a result, formatR is no longer Suggested. Trailing comments -- inline and on their own line -- are also now retained (#89, #91, #114, @jennybc and @jimhester). @@ -273,13 +276,13 @@ This was a non-functioning release created by CRAN maintainers by commenting out * `reprex()` gains optional arguments `opts_chunk` and `opts_knit`, taking named list as input, in order to supplement or override default knitr chunk and package options, respectively. (#33) - This made the explicit `upload.fun` argument unnecessary, so it's gone. The `upload.fun` option defaults to `knitr::imgur_upload`, which means figures produced by the reprex will be uploaded to [imgur.com](https://imgur.com/) and the associated image syntax will be put into the Markdown, e.g. `![](https://i.imgur.com/QPU5Cg9.png)`. (#15 @paternogbc) - + * Order of `reprex()` arguments has changed. * `reprex()` gains the `si` argument to request that `devtools::session_info()` or `sessionInfo()` be appended to reprex code (#6 @dgrtwo). When `si = TRUE` and `venue = "gh"` (the default), session info is wrapped in a collapsible details tag. See [an example](https://github.com/tidyverse/reprex/issues/55) (#55). * Reprex code can be provided as an R expression. (#6 @dgrtwo, #35) - + * `reprex()` uses clipboard functionality from [`clipr`](https://CRAN.R-project.org/package=clipr) and thus should work on Windows and suitably prepared Unix-like systems, in addition to Mac OS. (#16 @mdlincoln) # reprex 0.0.0.9000 diff --git a/R/reprex_impl.R b/R/reprex_impl.R index ee4a041..cfa320b 100644 --- a/R/reprex_impl.R +++ b/R/reprex_impl.R @@ -113,7 +113,9 @@ reprex_impl <- function( # i.e., consider if the user provided any path info reprex_path("Writing reprex file:", reprex_file) } - expose_reprex_output(reprex_file, rtf = (venue == "rtf")) + if (html_preview || reprex_clipboard()) { + expose_reprex_output(reprex_file, rtf = (venue == "rtf")) + } invisible(read_lines(reprex_file)) } diff --git a/R/reprex_upload.R b/R/reprex_upload.R new file mode 100644 index 0000000..2200569 --- /dev/null +++ b/R/reprex_upload.R @@ -0,0 +1,60 @@ +#' Upload reprex as gist +#' +#' Uses [`gh::gh()`] to upload a reprex to GitHub gists. +#' +#' @param reprex character: reprex as created by [`reprex()`] +#' @inheritDotParams gh::gh +#' @param gist_description character: description of gist, default "" +#' @param file_name character: file_name to create, default NULL will generate +#' random filename +#' @param public boolean: whether to make gist public, default TRUE +#' +#' @seealso +#' @family upload +#' +#' @examplesIf interactive() +#' reprex(print(pi), html_preview = FALSE) |> +#' upload_gist() +#' +#' @return uploads to GitHub gist and invisibly returns URL of created gist +#' @export +upload_gist <- function( + reprex, + ..., + gist_description = "", + file_name = NULL, + public = TRUE +) { + rlang::check_installed("gh") + + if (is.null(file_name)) { + file_name <- paste0( + format(Sys.Date(), format = "%Y%m%d"), + "_", + sample(adjective_animal, 1), + ".R" + ) + } + + stopifnot( + is.character(reprex) && length(reprex) > 0, + is_string(file_name), + is_string(gist_description), + is_bool(public) + ) + + gist_info <- gh::gh( + "POST /gists", + public = public, + description = gist_description, + files = setNames( + list(list(content = paste(reprex, collapse = "\n"))), + file_name + ), + ... + ) + + cli::cli_alert_success("Uploaded reprex to {.url {gist_info$html_url}}") + + return(invisible(gist_info$html_url)) +} diff --git a/_pkgdown.yml b/_pkgdown.yml index e4b2262..3c780da 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -27,6 +27,7 @@ reference: - reprex_addin - reprex_venue - reprex_locale + - upload_gist - title: Going backwards contents: - reprex_clean diff --git a/man/upload_gist.Rd b/man/upload_gist.Rd new file mode 100644 index 0000000..85ab63b --- /dev/null +++ b/man/upload_gist.Rd @@ -0,0 +1,95 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/reprex_upload.R +\name{upload_gist} +\alias{upload_gist} +\title{Upload reprex as gist} +\usage{ +upload_gist( + reprex, + ..., + gist_description = "", + file_name = NULL, + public = TRUE +) +} +\arguments{ +\item{reprex}{character: reprex as created by \code{\link[=reprex]{reprex()}}} + +\item{...}{ + Arguments passed on to \code{\link[gh:gh]{gh::gh}} + \describe{ + \item{\code{endpoint}}{GitHub API endpoint. Must be one of the following forms: +\itemize{ +\item \verb{METHOD path}, e.g. \code{GET /rate_limit}, +\item \code{path}, e.g. \verb{/rate_limit}, +\item \verb{METHOD url}, e.g. \verb{GET https://api.github.com/rate_limit}, +\item \code{url}, e.g. \verb{https://api.github.com/rate_limit}. +} + +If the method is not supplied, will use \code{.method}, which defaults +to \code{"GET"}.} + \item{\code{per_page,.per_page}}{Number of items to return per page. If omitted, +will be substituted by \code{max(.limit, 100)} if \code{.limit} is set, +otherwise determined by the API (never greater than 100).} + \item{\code{.token}}{Authentication token. Defaults to \code{\link[gh:gh_token]{gh_token()}}.} + \item{\code{.destfile}}{Path to write response to disk. If \code{NULL} (default), +response will be processed and returned as an object. If path is given, +response will be written to disk in the form sent. gh writes the +response to a temporary file, and renames that file to \code{.destfile} +after the request was successful. The name of the temporary file is +created by adding a \verb{-.gh-tmp} suffix to it, where \verb{} +is an ASCII string with random characters. gh removes the temporary +file on error.} + \item{\code{.overwrite}}{If \code{.destfile} is provided, whether to overwrite an +existing file. Defaults to \code{FALSE}. If an error happens the original +file is kept.} + \item{\code{.api_url}}{Github API url (default: \url{https://api.github.com}). Used +if \code{endpoint} just contains a path. Defaults to \code{GITHUB_API_URL} +environment variable if set.} + \item{\code{.method}}{HTTP method to use if not explicitly supplied in the +\code{endpoint}.} + \item{\code{.limit}}{Number of records to return. This can be used +instead of manual pagination. By default it is \code{NULL}, +which means that the defaults of the GitHub API are used. +You can set it to a number to request more (or less) +records, and also to \code{Inf} to request all records. +Note, that if you request many records, then multiple GitHub +API calls are used to get them, and this can take a potentially +long time.} + \item{\code{.accept}}{The value of the \code{Accept} HTTP header. Defaults to +\code{"application/vnd.github.v3+json"} . If \code{Accept} is given in +\code{.send_headers}, then that will be used. This parameter can be used to +provide a custom media type, in order to access a preview feature of +the API.} + \item{\code{.send_headers}}{Named character vector of header field values +(except \code{Authorization}, which is handled via \code{.token}). This can be +used to override or augment the default \code{User-Agent} header: +\code{"https://github.com/r-lib/gh"}.} + \item{\code{.progress}}{Whether to show a progress indicator for calls that +need more than one HTTP request.} + \item{\code{.params}}{Additional list of parameters to append to \code{...}. +It is easier to use this than \code{...} if you have your parameters in +a list already.} + \item{\code{.max_wait}}{Maximum number of seconds to wait if rate limited. +Defaults to 10 minutes.} + \item{\code{.max_rate}}{Maximum request rate in requests per second. Set +this to automatically throttle requests.} + }} + +\item{gist_description}{character: description of gist, default ""} + +\item{file_name}{character: file_name to create, default NULL will generate +random filename} + +\item{public}{boolean: whether to make gist public, default TRUE} +} +\value{ +performs upload to destination, optionally browses to the target URL +} +\description{ +Upload reprex as gist +} +\seealso{ +\url{https://docs.github.com/en/rest/gists/gists?apiVersion=2022-11-28#create-a-gist} +} +\concept{upload}