clipr/0000755000175000017500000000000014205032505011511 5ustar nileshnileshclipr/MD50000644000175000017500000000323014205032505012017 0ustar nileshnilesh181aa9ef9ab3d47179ef0ac61f9487a8 *DESCRIPTION ac9216bb379e4dad41fcf47ac889c3d9 *NAMESPACE cf385d39494ed10dd9d878d04995bb04 *NEWS.md 203d99a65e5a8f20a339cad36becb76c *R/clipboard.R 9c75ab4e1359ffadc1c01d04b23b70e4 *R/clipr-package.R c3673645d26e307510e4b9092022ab7b *R/clipr_addin.R 78462ea037dad6dc795301e220542f80 *R/flat_str.R 51d1ae24d6964d1cad8299b7eba93360 *R/linux_clipboard.R 127954870f38794f4b2671fc08623ac6 *R/osx_clipboard.R 9f8d569f398395aafbe00886fca97306 *R/read_clip_tbl.R 453049b87c715b158f0d4ed13721acc4 *R/utils.R 5b9bbc21d44d557fedf004203fb90608 *R/win_clipboard.R 905e922f73475fc62d4032d8724dabbe *README.md f89ddfb42b231d789c0eb0b9541b0342 *build/vignette.rds 46750e778a5cdb4d65c347f8e43df623 *inst/doc/developing-with-clipr.R 84e33d5522227cda21ffbb2c43045ee9 *inst/doc/developing-with-clipr.Rmd ba0558ec5bfe2072966f71f336630b4e *inst/doc/developing-with-clipr.html c5d91ccff2716bd64fd3dbf351507c07 *inst/rstudio/addins.dcf f8c1c7e8cb9612e9c3d58455491fba2b *man/clear_clip.Rd a34df8a952f7cd6f09dd6b8102e132dc *man/clipr.Rd 9ff95009c8aa71675ae5d2e6513d0e64 *man/clipr_available.Rd 0441a3c453b0e77192e8f1cd05283f3b *man/read_clip.Rd 5b64a13295fa3c2125a2c1922e5648ae *man/read_clip_tbl.Rd a0b697c89ab1900ed68cf2a2bafe4912 *man/write_clip.Rd 9ad848459b1749b5a5a26c74d9db944e *man/write_last_clip.Rd ec77100d776b5924e0da206268d2517d *tests/testthat.R 51d8d49a44ccf1bc6771a64ecc6a4d22 *tests/testthat/setup.R 7e6583d8339882002b75ffacb6b2abd0 *tests/testthat/test-diagnostics.R c84adc3c418b98dd0b8be2db8c798452 *tests/testthat/test-render.R 9fb913b103141779463f88542abb69e1 *tests/testthat/test-systems.R 84e33d5522227cda21ffbb2c43045ee9 *vignettes/developing-with-clipr.Rmd clipr/NEWS.md0000644000175000017500000001316614203715604012624 0ustar nileshnilesh## clipr 0.8.0 - Add handlers for using the clipboard on systems using [Wayland](https://wayland.freedesktop.org/) via [wl-clipboard](https://github.com/bugaevc/wl-clipboard). Thank you to @nacnudus for the PR. - For R >= 4.2, `write_clip()` calls on Windows are modified to work well with R's shift to supporting UTF-8 as a native encoding on Windows. Thank you to @yutannihilation for the PR. Read more here: - Moved CI off of Travis and on to GitHub Actions - Clipr now has a pkgdown site at ## clipr 0.7.1 - Call xsel with the `--output` flag, which prevents RStudio from hanging when calling clipr functions on a system running certain Linux window managers. Thank you to @cgillespie and @kevinushey for identifying the bug and the solution, and to @hannahcgunderman for help in testing. ## clipr 0.7.0 Thank you to @jennybc for prompting these changes: - Before attempting to read/write form the clipboard, `clipr_available()` will first explicitly check if it is being run non-interactively, and if so, if the `CLIPR_ALLOW` environment variable has been set. This will hopefully prevent starting spurious Linux processes during CRAN tests. - Out of an abundance of caution, `read_clip()` now does the same interactive/envvar check that `write_clip()` does. - Some documentation clarifications ## clipr 0.6.0 Thank you to @wangyuchen for making the following suggestions: - To make clipr more pipe-friendly, `write_clip()` now defaults to `return_new = FALSE`, and will instead return the initial object that was passed in. To get the old behavior, pass `return_new = TRUE` - In an effort to make `write_clip()` and `read_clip_tbl()` more symmetrical, `write_clip()` now defaults to writing out row and column names when they exist. - Introduces `write_last_clip()`, a wrapper function for `write_clip(.Last.value)` ## clipr 0.5.0 - To comply with CRAN policy, `write_clip()` will now error by default if run in a non-interactive session. Non-interactive use must be explicitly enabled by setting an environment variable `CLIPR_ALLOW=TRUE`. - Documented that the default behavior when writing matrices to `write_clip()` is `col.names = FALSE` ## clipr 0.4.1 - Correct a formatting error by adding and separation character to tables when they are being written with rownames. ## clipr 0.4.0 - Introduces `dr_clipr()`, which gives informative suggestions for software and configuration requirements when accessing the clipboard on X11-based systems. ## clipr 0.3.3 - Due to poor testing and configuration options, clipr was not delivering on its promised support for xsel :( This has now been fixed, with more complete Travis tests, and some core fixes by @milesmcbain. ## clipr 0.3.2 - Suppress an erroneous warning on OS X / X11 systems when trying to write an empty string to the clipboard. - Fix error when `NA` is passed to `write_clip()`. This will now write `"NA"` to the clipboard. - Fix error when passing `NULL` or an empty vector (e.g. `character(0)`). This will now write `""` to the clipboard. ## clipr 0.3.1 - Fixes a breaking bug that caused `clipr_available` to erroneously return `FALSE`. Thank you to @krivit for catching this. - Introduces better testing of `clipr_available` to properly evaluate it on Travis CI. ## clipr 0.3.0 - Introduces `clipr_available` which checks to see if the system clipboard is writeable/readable. This may be useful if you are developing a package that relies on clipr and need to ensure that it will skip tests on machines (e.g. CRAN, Travis) where the system clipboard may not be available. Thank you to @jennybc for this suggestion. - Implements genuine testing of clipr functionality with thanks to some deft environment variable settings added by @jennybc. - Two RStudio addins: one to copy the _value_ returned when a highlighted expression is evaluated, and another that copies the _console output_. ## clipr 0.2.1 - Introduces `read_clip_tbl`, a convenience function that takes tab-delimited text from `read_clip` (such as that copied from a spreadsheet) and parses it with `read.table`. Thank you to Steve Simpson (@data-steve) for the original PR. - `write_clip(object_type = "table")` has a new internal implementation (writing to a temporary file rather than using `capture.output`) which should dramatically shorten the time it takes to write very large tables to the clipboard. Thank you to @r2evans for this suggestion. ## clipr 0.2.0 - Several changes to `write_clip` - The separator to be used when writing a character vector can now be explicitly declared using `breaks`. `breaks=NULL` will default to system-specific line breaks for both vectors and tables. - `write_clip` will default to formatting data.frames and matrices with `write.table`, allowing easy pasting of tabular objects into programs like Excel. Option `object_type="auto"` will check the object type to decide on the correct formatting, or the user may explicitly state `object_type="table"` or `object_type="character"`. - clipr will default to sane system-specific options for `write.table()`, however you may pass any custom desired options via `write_clip` - `return_new=TRUE` (the default behavior) will return the formatted character string that was passed to the system clipboard, while `write_clip(return_new=FALSE)` will return the original object. - Introduces `clear_clip`, a wrapper function for `write_clip("")` for easy clearing of the system clipboard. ## clipr 0.1.1 - Bug fix that removes the explicit test for "Linux" in favor of a check for "xclip" or "xsel" clipr/DESCRIPTION0000644000175000017500000000261314205032505013221 0ustar nileshnileshType: Package Package: clipr Title: Read and Write from the System Clipboard Version: 0.8.0 Authors@R: c( person("Matthew", "Lincoln", , "matthew.d.lincoln@gmail.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0002-4387-3384")), person("Louis", "Maddox", role = "ctb"), person("Steve", "Simpson", role = "ctb"), person("Jennifer", "Bryan", role = "ctb") ) Description: Simple utility functions to read from and write to the Windows, OS X, and X11 clipboards. License: GPL-3 URL: https://github.com/mdlincoln/clipr, http://matthewlincoln.net/clipr/ BugReports: https://github.com/mdlincoln/clipr/issues Imports: utils Suggests: covr, knitr, rmarkdown, rstudioapi (>= 0.5), testthat (>= 2.0.0) VignetteBuilder: knitr Encoding: UTF-8 Language: en-US RoxygenNote: 7.1.2 SystemRequirements: xclip (https://github.com/astrand/xclip) or xsel (http://www.vergenet.net/~conrad/software/xsel/) for accessing the X11 clipboard, or wl-clipboard (https://github.com/bugaevc/wl-clipboard) for systems using Wayland. NeedsCompilation: no Packaged: 2022-02-19 02:20:21 UTC; mlincoln Author: Matthew Lincoln [aut, cre] (), Louis Maddox [ctb], Steve Simpson [ctb], Jennifer Bryan [ctb] Maintainer: Matthew Lincoln Repository: CRAN Date/Publication: 2022-02-22 00:58:45 UTC clipr/README.md0000644000175000017500000000543614204051563013004 0ustar nileshnilesh # clipr [![CRAN status.](https://www.r-pkg.org/badges/version/clipr)](https://www.r-pkg.org/pkg/clipr) ![Downloads, grand total](https://cranlogs.r-pkg.org/badges/grand-total/clipr) [![R-CMD-check](https://github.com/mdlincoln/clipr/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/mdlincoln/clipr/actions/workflows/R-CMD-check.yaml) [![Coverage Status](https://img.shields.io/codecov/c/github/mdlincoln/clipr/master.svg)](https://codecov.io/github/mdlincoln/clipr?branch=master) Simple utility functions to read and write from the system clipboards of Windows, OS X, and Unix-like systems (which require either xclip or xsel.) ## Installation Install from CRAN ``` r install.packages("clipr") ``` Or try the development version ``` r remotes::install_github("mdlincoln/clipr") ``` ## Usage ``` r library("clipr") #> Welcome to clipr. See ?write_clip for advisories on writing to the clipboard in R. cb <- read_clip() ``` clipr is pipe-friendly, and will default to returning the same object that was passed in. ``` r res <- write_clip(c("Text", "for", "clipboard")) res #> [1] "Text" "for" "clipboard" ``` To capture the string that clipr writes to the clipboard, specify `return_new = TRUE`. Character vectors with length \> 1 will be collapsed with system-appropriate line breaks, unless otherwise specified ``` r cb <- write_clip(c("Text", "for", "clipboard"), return_new = TRUE) cb #> [1] "Text\nfor\nclipboard" cb <- write_clip(c("Text", "for", "clipboard"), breaks = ", ", return_new = TRUE) cb #> [1] "Text, for, clipboard" ``` `write_clip` also tries to intelligently handle data.frames and matrices, rendering them with `write.table` so that they can be pasted into a spreadsheet like Excel. ``` r tbl <- data.frame(a = c(1, 2, 3), b = c(4, 5, 6)) cb <- write_clip(tbl, return_new = TRUE) cb #> [1] "a\tb\n1\t4\n2\t5\n3\t6" ``` `read_clip_tbl` will try to parse clipboard contents from spreadsheets into data frames directly. ## Developing with clipr See the “Developing with clipr” vignette included with this package for advisories on writing code that calls clipr functions. ## Nice uses of clipr (a non-comprehensive list) 1. [reprex](https://github.com/tidyverse/reprex) by [@jennybc](https://github.com/jennybc) takes R code on the clipboard and renders a reproducible example from it, ready to then paste on to GitHub, Stack Overflow, or the like. 2. [datapasta](https://github.com/milesmcbain/datapasta) by [@milesmcbain](https://github.com/milesmcbain) eases the copying and pasting of R objects in and out of different sources (Excel, Google Sheets). ------------------------------------------------------------------------ [Matthew Lincoln](https://matthewlincoln.net) clipr/man/0000755000175000017500000000000014177762460012306 5ustar nileshnileshclipr/man/write_clip.Rd0000644000175000017500000000657114177762460014747 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/clipboard.R \name{write_clip} \alias{write_clip} \title{Write clipboard} \usage{ write_clip( content, object_type = c("auto", "character", "table"), breaks = NULL, eos = NULL, return_new = FALSE, allow_non_interactive = Sys.getenv("CLIPR_ALLOW", interactive()), ... ) } \arguments{ \item{content}{An object to be written to the system clipboard.} \item{object_type}{\code{\link[=write_clip]{write_clip()}} tries to be smart about writing objects in a useful manner. If passed a data.frame or matrix, it will format it using \code{\link[=write.table]{write.table()}} for pasting into an external spreadsheet program. It will otherwise coerce the object to a character vector. \code{auto} will check the object type, otherwise \code{table} or \code{character} can be explicitly specified.} \item{breaks}{The separator to be used between each element of the character vector being written. \code{NULL} defaults to writing system-specific line breaks between each element of a character vector, or each row of a table.} \item{eos}{The terminator to be written after each string, followed by an ASCII \code{nul}. Defaults to no terminator character, indicated by \code{NULL}.} \item{return_new}{If true, returns the rendered string; if false, returns the original object} \item{allow_non_interactive}{By default, clipr will throw an error if run in a non-interactive session. Set the environment variable \code{CLIPR_ALLOW=TRUE} in order to override this behavior.} \item{...}{Custom options to be passed to \code{\link[=write.table]{write.table()}} (if \code{x} is a table-like). Defaults to sane line-break and tab standards based on the operating system. By default, this will use \code{col.names = TRUE} if the table object has column names, and \code{row.names = TRUE} if the object has row names other than \verb{c("1", "2", "3"...)}. Override these defaults by passing arguments here.} } \value{ Invisibly returns the original object } \description{ Write a character vector to the system clipboard } \note{ On X11 systems, \code{\link[=write_clip]{write_clip()}} will cause either xclip (preferred) or xsel to be called. Be aware that, by design, these processes will fork into the background. They will run until the next paste event, when they will then exit silently. (See the man pages for \href{https://linux.die.net/man/1/xclip}{xclip} and \href{http://www.vergenet.net/~conrad/software/xsel/xsel.1x.html#notes}{xsel} for more on their behaviors.) However, this means that even if you terminate your R session after running \code{\link[=write_clip]{write_clip()}}, those processes will continue until you access the clipboard via another program. This may be expected behavior for interactive use, but is generally undesirable for non-interactive use. For this reason you must not run \code{\link[=write_clip]{write_clip()}} on CRAN, as the nature of xsel \href{https://github.com/mdlincoln/clipr/issues/38}{has caused issues in the past}. Call \code{\link[=clipr_available]{clipr_available()}} to safely check whether the clipboard is readable and writable. } \examples{ \dontrun{ text <- "Write to clipboard" write_clip(text) multiline <- c("Write", "to", "clipboard") write_clip(multiline) # Write # to # clipboard write_clip(multiline, breaks = ",") # write,to,clipboard tbl <- data.frame(a=c(1,2,3), b=c(4,5,6)) write_clip(tbl) } } clipr/man/write_last_clip.Rd0000644000175000017500000000072514177762460015765 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/clipboard.R \name{write_last_clip} \alias{write_last_clip} \title{Write contents of the last R expression to the clipboard} \usage{ write_last_clip(...) } \arguments{ \item{\ldots}{Pass other options to \code{\link[=write_clip]{write_clip()}}.} } \description{ Write contents of the last R expression to the clipboard } \note{ This is a wrapper function for \code{write_clip(.Last.value)} } clipr/man/clipr.Rd0000644000175000017500000000136714177762460013715 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/clipr-package.R \docType{package} \name{clipr} \alias{clipr} \title{clipr: Read and Write from the System Clipboard} \description{ Simple utility functions to read from and write to the Windows, OS X, and X11 clipboards. } \details{ The basic functions \code{\link[=read_clip]{read_clip()}} and \code{\link[=write_clip]{write_clip()}} wrap platform-specific functions for writing values from R to the system clipboard. \code{\link[=read_clip_tbl]{read_clip_tbl()}} will attempt to process the clipboard content like a table copied from a spreadsheet program. \code{\link[=clipr_available]{clipr_available()}} is useful when building packages that depend on clipr functionality. } clipr/man/clear_clip.Rd0000644000175000017500000000056714177762460014702 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/clipboard.R \name{clear_clip} \alias{clear_clip} \title{Clear clipboard} \usage{ clear_clip(...) } \arguments{ \item{\ldots}{Pass other options to \code{\link[=write_clip]{write_clip()}}.} } \description{ Clear the system clipboard. } \note{ This is a wrapper function for \code{write_clip("")} } clipr/man/clipr_available.Rd0000644000175000017500000000261714177762460015714 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils.R \name{clipr_available} \alias{clipr_available} \alias{dr_clipr} \title{Is the system clipboard available?} \usage{ clipr_available(...) dr_clipr(...) } \arguments{ \item{\ldots}{Pass other options to \code{\link[=write_clip]{write_clip()}}. Generally only used to pass the argument \code{allow_non_interactive_use = TRUE}.} } \value{ \code{clipr_available} returns a boolean value. Prints an informative message to the console with software and system configuration requirements if clipr is not available (invisibly returns the same string) } \description{ Checks to see if the system clipboard is write-able/read-able. This may be useful if you are developing a package that relies on clipr and need to ensure that it will skip tests on machines (e.g. CRAN, Travis) where the system clipboard may not be available. } \note{ This will automatically return \code{FALSE}, without even performing the check, if you are running in a non-interactive session. If you must call this non-interactively, be sure to call using \code{clipr_available(allow_non_interactive = TRUE)}, or by setting the environment variable \code{CLIPR_ALLOW=TRUE}. \strong{Do not attempt to run clipr non-interactively on CRAN; this will result in a failed build!} } \examples{ \dontrun{ # When using testthat: library(testthat) skip_if_not(clipr_available()) } } clipr/man/read_clip.Rd0000644000175000017500000000157714177762460014531 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/clipboard.R \name{read_clip} \alias{read_clip} \title{Read clipboard} \usage{ read_clip(allow_non_interactive = Sys.getenv("CLIPR_ALLOW", interactive())) } \arguments{ \item{allow_non_interactive}{By default, clipr will throw an error if run in a non-interactive session. Set the environment variable \code{CLIPR_ALLOW=TRUE} in order to override this behavior.} } \value{ A character vector with the contents of the clipboard. If the system clipboard is empty, returns NULL } \description{ Read the contents of the system clipboard into a character vector. } \note{ \code{\link[=read_clip]{read_clip()}} will not try to guess at how to parse copied text. If you are copying tabular data, it is suggested that you use \code{\link[=read_clip_tbl]{read_clip_tbl()}}. } \examples{ \dontrun{ clip_text <- read_clip() } } clipr/man/read_clip_tbl.Rd0000644000175000017500000000226114177762460015361 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/read_clip_tbl.R \name{read_clip_tbl} \alias{read_clip_tbl} \title{Transforms output of \code{\link[=read_clip]{read_clip()}} into data frame.} \usage{ read_clip_tbl(x = read_clip(), ...) } \arguments{ \item{x}{Defaults to reading from the clipboard, but can be substituted by a character vector already generated by \code{\link[=read_clip]{read_clip()}}.} \item{\ldots}{Options to pass to \code{\link[=read.table]{read.table()}}. The following \code{\link[=read.table]{read.table()}} arguments will be passed by default, but can be overridden by specifying them when calling \code{read_clip_tbl}: \describe{ \item{\code{header}}{\code{TRUE}} \item{\code{sep}}{\code{"\\t"}} \item{\code{row.names}}{\code{1}} \item{\code{stringsAsFactors}}{\code{FALSE}} \item{\code{na.strings}}{\code{c("NA", "")}} \item{\code{strip.white}}{\code{TRUE}} }} } \value{ A data frame with the contents of the clipboard. If the system clipboard is empty, returns \code{NULL} } \description{ Transforms clipped content into a data frame by putting \code{\link[=read_clip]{read_clip()}} output by using \code{\link[=read.table]{read.table()}}. } clipr/vignettes/0000755000175000017500000000000014204051745013527 5ustar nileshnileshclipr/vignettes/developing-with-clipr.Rmd0000644000175000017500000000631614200536221020406 0ustar nileshnilesh--- title: "Developing with clipr" author: "Matthew Lincoln" date: "`r Sys.Date()`" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Developing with clipr} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` ## Calling clipr safely To check whether the system has been properly configured to allow access to the clipboard, you may run `clipr_available()` which will either return `TRUE` or `FALSE`. This will be particularly useful for Linux-based systems, where clipr's functionality depends on the installation of additional software. If you wish to display system requirements and configuration messages to X11 and Wayland users, `dr_clipr()` will print these. ## Interactive & non-interactive use If you use clipr in your own package, **you must not try to call it in non-interactive sessions**, as this goes against [CRAN repository policy](https://cran.r-project.org/web/packages/policies.html): > Packages should not write in the user’s home filespace (including clipboards), nor anywhere else on the file system apart from the R session’s temporary directory (or during installation in the location pointed to by TMPDIR: and such usage should be cleaned up). Installing into the system’s R installation (e.g., scripts to its bin directory) is not allowed. > > Limited exceptions may be allowed in interactive sessions if the package obtains confirmation from the user. For this reason, `write_clip()` will error by default in non-interactive use, which includes CRAN tests. If you want to use `write_clip()` non-interactively, you may either set the environment variable `CLIPR_ALLOW=TRUE` or call `write_clip(..., allow_non_interactive = TRUE)`. ## Testing on CRAN and CI A few best practices will also help you responsibly test your clipr-using package on headless systems like CRAN or other testing infrastructure like Travis: 1. Examples that will try to use `read_clip()` or `write_clip()` ought to be wrapped in `\dontrun{}` 2. Tests calling clipr should be conditionally skipped, based on the output of `clipr_available()`. This is necessary to pass CRAN checks, as otherwise `write_clip` will error out. 3. If you are using a headless system to check your package build on Linux, consult the [GitHub Actions workflow](https://github.com/mdlincoln/clipr/blob/main/.github/workflows/R-CMD-check.yaml) for this package, which includes code for setting the `DISPLAY` and `CLIPR_ALLOW` environment variables, installing `xclip` and `xsel`, and running a pre-build script that will set up `xclip`/`xsel` to run headlessly under XVFB. ## Using clipr with Shiny clipr won't do what you expect when you call it with Shiny. clipr talks to the clipboard of the _system that is running R_. If you create a Shiny app and tell one of its functions to either read from or write to the clipboard, it can only access the clipboard of the server it is running on. R running on the _remote_ server has no way to access the _local_ clipboard belonging to your end user. However, you can instruct the user's _internet browser_ to write to the user's clipboard by using [rclipboard](https://cran.r-project.org/package=rclipboard). clipr/build/0000755000175000017500000000000014204051745012616 5ustar nileshnileshclipr/build/vignette.rds0000644000175000017500000000033214204051745015153 0ustar nileshnileshb```b`adb`b2 1# 'LI-K/K-,M,( MAS(WRVH i%9h*q t0XD90!icKM-F3% 5/$~hZ8S+`zP԰Aհe ,s\ܠL t7`~΢r=xA$Gs=ʕXVr7L:clipr/tests/0000755000175000017500000000000014177762460012675 5ustar nileshnileshclipr/tests/testthat/0000755000175000017500000000000014205032505014513 5ustar nileshnileshclipr/tests/testthat/test-render.R0000644000175000017500000001304014177762460017112 0ustar nileshnileshcontext("Clipr read and write") test_that("single NA vectors don't cause error", { skip_if_not(is_clipr_available, skip_msg) expect_equivalent(write_clip(NA_character_), NA_character_) expect_equivalent(write_clip(NA_character_, return_new = TRUE), "NA") expect_warning(write_clip(NA)) expect_warning(write_clip(NA_integer_)) expect_warning(write_clip(NA_real_)) expect_warning(write_clip(NA_complex_)) }) test_that("empty character in write_clip() causes no erroneous warning", { skip_if_not(is_clipr_available, skip_msg) expect_equivalent(write_clip(""), "") expect_warning(null_res <- write_clip(NULL)) expect_equivalent(null_res, NULL) expect_warning(null_new_res <- write_clip(NULL, return_new = TRUE)) expect_equivalent(null_new_res, "") expect_equivalent(write_clip(character(0)), character(0)) expect_equivalent(write_clip(character(0), return_new = TRUE), "") expect_warning(empty_res <- write_clip(integer(0))) expect_equivalent(empty_res, integer(0)) expect_warning(empty_new_res <- write_clip(integer(0), return_new = TRUE)) expect_equivalent(empty_new_res, "") expect_silent(clear_clip()) }) test_that("Render character vectors", { skip_if_not(is_clipr_available, skip_msg) single <- "hello, world!" expect_equivalent(write_clip(single), single) }) test_that("Render default multiline vectors", { skip_if_not(is_clipr_available, skip_msg) multiline <- c("hello", "world!") inv_out <- write_clip(multiline, return_new = TRUE) if (sys_type() == "Windows") { expect_equivalent(inv_out, "hello\r\nworld!") } else { expect_equivalent(inv_out, "hello\nworld!") } expect_equivalent(read_clip(), multiline) }) test_that("Render custom multiline vectors", { skip_if_not(is_clipr_available, skip_msg) multiline <- c("hello", "world!") inv_out <- write_clip(multiline, breaks = ", ", return_new = TRUE) expect_equivalent(inv_out, "hello, world!") expect_equivalent(read_clip(), inv_out) }) test_that("Render default data.frames", { skip_if_not(is_clipr_available, skip_msg) tbl <- data.frame(a = c(1,2,3), b = c(4,5,6)) inv_out <- write_clip(tbl, return_new = TRUE) if (sys_type() == "Windows") { expect_equivalent(inv_out, "a\tb\r\n1\t4\r\n2\t5\r\n3\t6") } else { expect_equivalent(inv_out, "a\tb\n1\t4\n2\t5\n3\t6") } expect_equal(read_clip_tbl(), tbl) }) test_that("Probable rownames are read", { skip_if_not(is_clipr_available, skip_msg) write_clip(mtcars) expect_equal(read_clip_tbl(), mtcars) }) test_that("Render custom data.frames", { skip_if_not(is_clipr_available, skip_msg) tbl <- data.frame(a = c(1,2,3), b = c(4,5,6)) inv_out <- write_clip(tbl, sep = ",", return_new = TRUE) if (sys_type() == "Windows") { expect_equivalent(inv_out, "a,b\r\n1,4\r\n2,5\r\n3,6") } else { expect_equivalent(inv_out, "a,b\n1,4\n2,5\n3,6") } expect_equivalent(read_clip(), c("a,b", "1,4", "2,5", "3,6")) }) test_that("Render matricies", { skip_if_not(is_clipr_available, skip_msg) tbl <- matrix(c(1, 2, 3, 4, 5, 6), nrow = 3, ncol = 2) inv_out <- write_clip(tbl, return_new = TRUE) if (sys_type() == "Windows") { expect_equivalent(inv_out, "1\t4\r\n2\t5\r\n3\t6") } else { expect_equivalent(inv_out, "1\t4\n2\t5\n3\t6") } expect_equivalent(read_clip(), c("1\t4", "2\t5", "3\t6")) }) test_that("Render custom matricies", { skip_if_not(is_clipr_available, skip_msg) tbl <- matrix(c(1, 2, 3, 4, 5, 6), nrow = 3, ncol = 2) inv_out <- write_clip(tbl, sep = ",", return_new = TRUE) if (sys_type() == "Windows") { expect_equivalent(inv_out, "1,4\r\n2,5\r\n3,6") } else { expect_equivalent(inv_out, "1,4\n2,5\n3,6") } expect_equivalent(read_clip(), c("1,4", "2,5", "3,6")) }) test_that("Render tables read from clipboard as data.frames", { skip_if_not(is_clipr_available, skip_msg) inv_out <- write_clip(iris[1:2, 1:4], return_new = TRUE) expect_equivalent(read_clip_tbl(), iris[1:2, 1:4]) }) test_that("Tables written with rownames add extra space for column names", { skip_if_not(is_clipr_available, skip_msg) d <- matrix(1:4, 2) rownames(d) <- c('a','b') colnames(d) <- c('c','d') df <- data.frame(c = c(1, 2), d = c(3, 4)) rownames(df) <- c('a', 'b') mat_rnames_out <- write_clip(d, row.names = TRUE, col.names = FALSE, return_new = TRUE) df_rnames_out <- write_clip(df, row.names = TRUE, col.names = FALSE, return_new = TRUE) if (sys_type() == "Windows") { expect_equivalent(mat_rnames_out, "a\t1\t3\r\nb\t2\t4") expect_equivalent(df_rnames_out, "a\t1\t3\r\nb\t2\t4") } else { expect_equivalent(mat_rnames_out, "a\t1\t3\nb\t2\t4") expect_equivalent(df_rnames_out, "a\t1\t3\nb\t2\t4") } mat_bnames_out <- write_clip(d, row.names = TRUE, col.names = TRUE, return_new = TRUE) df_bnames_out <- write_clip(df, row.names = TRUE, col.names = TRUE, return_new = TRUE) if (sys_type() == "Windows") { expect_equivalent(mat_bnames_out, "\tc\td\r\na\t1\t3\r\nb\t2\t4") expect_equivalent(df_bnames_out, "\tc\td\r\na\t1\t3\r\nb\t2\t4") } else { expect_equivalent(mat_bnames_out, "\tc\td\na\t1\t3\nb\t2\t4") expect_equivalent(df_bnames_out, "\tc\td\na\t1\t3\nb\t2\t4") } mat_nonames_out <- write_clip(d, row.names = FALSE, col.names = FALSE, return_new = TRUE) df_nonames_out <- write_clip(df, row.names = FALSE, col.names = FALSE, return_new = TRUE) if (sys_type() == "Windows") { expect_equivalent(mat_nonames_out, "1\t3\r\n2\t4") expect_equivalent(df_nonames_out, "1\t3\r\n2\t4") } else { expect_equivalent(mat_nonames_out, "1\t3\n2\t4") expect_equivalent(df_nonames_out, "1\t3\n2\t4") } }) clipr/tests/testthat/test-diagnostics.R0000644000175000017500000000414314177762460020146 0ustar nileshnileshcontext("diagnostics") test_that("clipr_available fails when DISPLAY is not configured; succeeds when it is", { # Only run this test on Github Actions skip_if_not(identical(Sys.getenv("GITHUB_ACTIONS"), "true")) # If this envar hasn't been set, confirm that is_clipr_available will be false # and write_clip will error if (identical(Sys.getenv("CLIPR_ALLOW"), "")) { expect_false(is_clipr_available) expect_error(write_clip("test")) } else { if (identical(Sys.getenv("CLIP_TYPE"), "none")) expect_false(is_clipr_available) if (identical(Sys.getenv("CLIP_TYPE"), "xclip")) expect_true(is_clipr_available) if (identical(Sys.getenv("CLIP_TYPE"), "xsel")) expect_true(is_clipr_available) if (identical(Sys.getenv("CLIP_TYPE"), "wayland")) expect_true(is_clipr_available) } }) test_that("dr_clipr provides informative messages", { if (identical(Sys.getenv("CLIPR_ALLOW"), "")) { expect_message(dr_clipr(), "CLIPR_ALLOW", fixed = TRUE) } else { if (identical(Sys.getenv("CLIP_TYPE"), "xclip")) expect_message(dr_clipr(), msg_clipr_available(), fixed = TRUE) if (identical(Sys.getenv("CLIP_TYPE"), "xsel")) expect_message(dr_clipr(), msg_clipr_available(), fixed = TRUE) if (identical(Sys.getenv("CLIP_TYPE"), "wayland")) expect_message(dr_clipr(), msg_clipr_available(), fixed = TRUE) if (identical(Sys.getenv("CLIP_TYPE"), "none")) expect_message(dr_clipr(), msg_no_clipboard(), fixed = TRUE) if (identical(Sys.getenv("CLIP_TYPE"), "nodisplay")) expect_message(dr_clipr(), msg_no_display(), fixed = TRUE) expect_true(grepl("has read/write access", msg_clipr_available())) expect_true(grepl("requires 'xclip'", msg_no_clipboard())) expect_true(grepl("requires that the DISPLAY", msg_no_display())) } }) test_that("Unavailable clipboard throws warning", { if (!is_clipr_available) { expect_error(write_clip("a")) } }) test_that("clipr_available() does not overwrite existing contents", { skip_if_not(is_clipr_available, skip_msg) write_clip("z") clipr_available() expect_equal(read_clip(), "z") }) clipr/tests/testthat/test-systems.R0000644000175000017500000000151614177762460017347 0ustar nileshnileshcontext("systems") test_that("utility checking works on Linux-like", { if (identical(Sys.getenv("CLIP_TYPE"), "xclip")) { expect_true(has_xclip()) expect_false(has_xsel()) expect_false(has_wl_clipboard()) } if (identical(Sys.getenv("CLIP_TYPE"), "xsel")) { expect_false(has_xclip()) expect_true(has_xsel()) expect_false(has_wl_clipboard()) } if (identical(Sys.getenv("CLIP_TYPE"), "wayland")) { expect_false(has_xclip()) expect_false(has_xsel()) expect_true(has_wl_clipboard()) } if (identical(Sys.getenv("CLIP_TYPE"), "none")) { expect_false(has_xclip()) expect_false(has_xsel()) expect_false(has_wl_clipboard()) } if (identical(Sys.getenv("CLIP_TYPE"), "nodisplay")) { expect_error(has_xclip()) expect_false(has_xsel()) expect_false(has_wl_clipboard()) } }) clipr/tests/testthat/setup.R0000644000175000017500000000027614177762460016025 0ustar nileshnilesh# Setup for all following tests skip_msg <- "System clipboard is not available - skipping test." is_clipr_available <- clipr_available() message("Is clipr available?: ", is_clipr_available) clipr/tests/testthat.R0000644000175000017500000000006614177762460014662 0ustar nileshnileshlibrary(testthat) library(clipr) test_check("clipr") clipr/R/0000755000175000017500000000000014200235326011713 5ustar nileshnileshclipr/R/linux_clipboard.R0000644000175000017500000000653514177762460015246 0ustar nileshnilesh# Determine if a given utility is installed AND accessible # Takes a character vector whose first element is the name of the # utility executable and whose subsequent elements are command-line # arguments to the utility for the test run. has_util <- function(util_test) { if (nzchar(Sys.which(util_test[1]))) { # If utility is accessible, check that DISPLAY can be opened. try_res <- tryCatch(system2(util_test[1], util_test[-1], stdout = TRUE, stderr = TRUE), error = function(c) { print(c) return(FALSE) }, warning = function(c) { print(c) return(FALSE) } ) # In the case of an error/warning on trying the function, then the util is # not available if (identical(try_res, FALSE)) { notify_no_display() } else { TRUE } } else { FALSE } } # Determine if system has 'xclip' installed AND it's accessible has_xclip <- function() has_util(c("xclip", "-o", "-selection", "clipboard")) # Determine if system has 'xsel' installed has_xsel <- function() has_util(c("xsel", "--clipboard", "--output")) # Determine if system has both 'wl-paste' and 'wl-copy' installed has_wl_clipboard <- function() has_wl_paste() & has_wl_copy() # Determine if system has 'wl-paste' installed has_wl_paste <- function() has_util(c("wl-paste", "--primary")) # Determine if system has 'wl-paste' installed has_wl_copy <- function() has_util(c("wl-copy", "--primary")) # Stop read/write and return an error of missing clipboard software. notify_no_cb <- function() { stop(msg_no_clipboard(), call. = FALSE) } notify_no_display <- function() { stop(msg_no_display(), call. = FALSE) } # Helper function to read from the X11 clipboard # # Requires the utility 'xclip' or 'xsel' when using X11, or 'wl-paste' when # using Wayland. This function will stop with an error if neither is found. # Adapted from: # https://github.com/mrdwab/overflow-mrdwab/blob/master/R/readClip.R and: # https://github.com/jennybc/reprex/blob/master/R/clipboard.R X11_read_clip <- function() { if (has_xclip()) { con <- pipe("xclip -o -selection clipboard") } else if (has_xsel()) { con <- pipe("xsel --clipboard --output") } else if (has_wl_paste()) { con <- pipe("wl-paste") } else { notify_no_cb() } content <- scan(con, what = character(), sep = "\n", blank.lines.skip = FALSE, quiet = TRUE) close(con) return(content) } # Helper function to write to the X11 clipboard # # Requires the utility 'xclip' or 'xsel' when using X11, or 'wl-copy' when using # Wayland. This function will stop with an error if neither is found. Adapted # from https://github.com/mrdwab/overflow-mrdwab/blob/master/R/writeClip.R # # Targets "primary" and "clipboard" clipboards if using xclip, see: # http://unix.stackexchange.com/a/69134/89254 X11_write_clip <- function(content, object_type, breaks, eos, return_new, ...) { if (has_xclip()) { con <- pipe("xclip -i -sel p -f | xclip -i -sel c", "w") } else if (has_xsel()) { con <- pipe("xsel --clipboard --input", "w") } else if (has_wl_copy()) { con <- pipe("wl-copy", "w") } else { notify_no_cb() } .dots <- list(...) write_nix(content, object_type, breaks, eos, return_new, con, .dots) } clipr/R/flat_str.R0000644000175000017500000000416314177762460013701 0ustar nileshnilesh# Check object type to determine if it will be handled as a simple table or as a # character vector render_object <- function(content, object_type, breaks, .dots) { if (object_type == "auto") object_type <- eval_object(content) switch(object_type, "table" = table_str(content, breaks, .dots), "character" = flat_str(content, breaks)) } eval_object <- function(content) { ifelse(is.data.frame(content) | is.matrix(content), "table", "character") } # If object is a table, default to a multiline string with tab separators table_str <- function(content, breaks, .dots) { # Take the system-specific collapse out of the list .dots$x <- content .dots$sep <- .dots$sep .dots$quote <- ifelse(is.null(.dots$quote), FALSE, .dots$quote) .dots$na <- ifelse(is.null(.dots$na), "", .dots$na) .dots$col.names <- ifelse(is.null(.dots$col.names), !is.null(colnames(content)), .dots$col.names) # Check if dataframe rownames are anything different than the default numbered names numbered_rownames <- all(rownames(content) == as.character(seq_along(rownames(content)))) .dots$row.names <- ifelse(is.null(.dots$row.names), ifelse(numbered_rownames, FALSE, !is.null(rownames(content))), .dots$row.names) # Writing to and reading from a temp file is much faster than using capture.output tbl_file <- tempfile() .dots$file = tbl_file do.call(utils::write.table, .dots) read_tbl <- paste0(readLines(tbl_file), collapse = breaks) unlink(tbl_file) # If row.names = TRUE and col.names = TRUE, add additional sep character to # the start of the table if (.dots$row.names & .dots$col.names) { read_tbl <- paste0(.dots$sep, read_tbl) } return(read_tbl) } # Helper function to flatten content into 1-tuple character vector (i.e. a # string) flat_str <- function(content, breaks) { if (typeof(content) != "character") { warning("Coercing content to character") content <- as.character(content) } if (length(content) < 1) { content <- "" } else if (length(content) > 1) { content <- paste0(content, collapse = breaks) } else if (is.na(content)) { content <- "NA" } return(content) } clipr/R/clipboard.R0000644000175000017500000001253114177762460014020 0ustar nileshnilesh#' Read clipboard #' #' Read the contents of the system clipboard into a character vector. #' #' @param allow_non_interactive By default, clipr will throw an error if run in #' a non-interactive session. Set the environment variable #' `CLIPR_ALLOW=TRUE` in order to override this behavior. #' #' @return A character vector with the contents of the clipboard. If the system #' clipboard is empty, returns NULL #' #' @note [read_clip()] will not try to guess at how to parse copied text. If #' you are copying tabular data, it is suggested that you use #' [read_clip_tbl()]. #' #' @examples #' \dontrun{ #' clip_text <- read_clip() #' } #' #' @export read_clip <- function(allow_non_interactive = Sys.getenv("CLIPR_ALLOW", interactive())) { if (allow_non_interactive != "TRUE") error_interactive() # Determine system type sys.type <- sys_type() # Use the appropriate handler function chosen_read_clip <- switch(sys.type, "Darwin" = osx_read_clip, "Windows" = win_read_clip, X11_read_clip ) content <- chosen_read_clip() if (length(content) == 0) { warning("System clipboard contained no readable text. Returning NULL.") return(NULL) } content } #' Write clipboard #' #' Write a character vector to the system clipboard #' #' @inheritParams read_clip #' #' @param content An object to be written to the system clipboard. #' @param object_type [write_clip()] tries to be smart about writing objects in a #' useful manner. If passed a data.frame or matrix, it will format it using #' [write.table()] for pasting into an external spreadsheet program. #' It will otherwise coerce the object to a character vector. `auto` will #' check the object type, otherwise `table` or `character` can be #' explicitly specified. #' @param breaks The separator to be used between each element of the character #' vector being written. `NULL` defaults to writing system-specific line #' breaks between each element of a character vector, or each row of a table. #' @param eos The terminator to be written after each string, followed by an #' ASCII `nul`. Defaults to no terminator character, indicated by #' `NULL`. #' @param return_new If true, returns the rendered string; if false, returns the #' original object #' @param ... Custom options to be passed to [write.table()] (if `x` is a #' table-like). Defaults to sane line-break and tab standards based on the #' operating system. By default, this will use `col.names = TRUE` if the table #' object has column names, and `row.names = TRUE` if the object has row names #' other than `c("1", "2", "3"...)`. Override these defaults by passing #' arguments here. #' #' @note On X11 systems, [write_clip()] will cause either xclip (preferred) or #' xsel to be called. Be aware that, by design, these processes will fork into #' the background. They will run until the next paste event, when they will #' then exit silently. (See the man pages for #' [xclip](https://linux.die.net/man/1/xclip) and #' [xsel](http://www.vergenet.net/~conrad/software/xsel/xsel.1x.html#notes) #' for more on their behaviors.) However, this means that even if you #' terminate your R session after running [write_clip()], those processes will #' continue until you access the clipboard via another program. This may be #' expected behavior for interactive use, but is generally undesirable for #' non-interactive use. For this reason you must not run [write_clip()] on #' CRAN, as the nature of xsel [has caused issues in the #' past](https://github.com/mdlincoln/clipr/issues/38). #' #' Call [clipr_available()] to safely check whether the clipboard is readable #' and writable. #' #' @return Invisibly returns the original object #' #' @examples #' \dontrun{ #' text <- "Write to clipboard" #' write_clip(text) #' #' multiline <- c("Write", "to", "clipboard") #' write_clip(multiline) #' # Write #' # to #' # clipboard #' #' write_clip(multiline, breaks = ",") #' # write,to,clipboard #' #' tbl <- data.frame(a=c(1,2,3), b=c(4,5,6)) #' write_clip(tbl) #' } #' #' @export write_clip <- function(content, object_type = c("auto", "character", "table"), breaks = NULL, eos = NULL, return_new = FALSE, allow_non_interactive = Sys.getenv("CLIPR_ALLOW", interactive()), ...) { if (allow_non_interactive != "TRUE") error_interactive() object_type <- match.arg(object_type) # Determine system type sys.type <- sys_type() # Choose an operating system-specific function (stop with error if not # recognized) chosen_write_clip <- switch(sys.type, "Darwin" = osx_write_clip, "Windows" = win_write_clip, X11_write_clip ) # Supply the clipboard content to write and options list to this function invisible(chosen_write_clip(content, object_type, breaks, eos, return_new, ...)) } #' Clear clipboard #' #' Clear the system clipboard. #' #' @param \ldots Pass other options to [write_clip()]. #' #' @note This is a wrapper function for `write_clip("")` #' #' @export clear_clip <- function(...) { write_clip(content = "", ...) } #' Write contents of the last R expression to the clipboard #' #' @param \ldots Pass other options to [write_clip()]. #' #' @note This is a wrapper function for `write_clip(.Last.value)` #' @export write_last_clip <- function(...) { write_clip(.Last.value, ...) } clipr/R/clipr_addin.R0000644000175000017500000000056414177762460014334 0ustar nileshnileshclipr_result <- function() { context <- rstudioapi::getActiveDocumentContext() expr_object <- eval(parse(text = context$selection[[1]]$text)) write_clip(expr_object) } clipr_output <- function() { context <- rstudioapi::getActiveDocumentContext() expr_object <- eval(parse(text = context$selection[[1]]$text)) write_clip(utils::capture.output(expr_object)) } clipr/R/win_clipboard.R0000644000175000017500000000221514200235326014652 0ustar nileshnilesh# Helper function to read from the Windows clipboard win_read_clip <- function() { # On R >= 4.2 (built with UCRT), the default encoding is UTF-8 even on Windows. # To avoid the encoding mismatch garbles texts, use 13 (CF_UNICODETEXT), format <- if (identical(R.version$crt, "ucrt")) 13 else 1 utils::readClipboard(format = format) } # Helper function to write to the Windows clipboard win_write_clip <- function(content, object_type, breaks, eos, return_new, ...) { format <- if (identical(R.version$crt, "ucrt")) 13 else 1 .dots <- list(...) # If no custom line separator has been specified, use Windows's default # newline character '\r\n' breaks <- ifelse(is.null(breaks), '\r\n', breaks) # If no custom tab separator for tables has been specified, use Windows's # default tab character: '\t' .dots$sep <- ifelse(is.null(.dots$sep), '\t', .dots$sep) # Pass the object to rendering functions before writing out to the clipboard rendered_content <- render_object(content, object_type, breaks, .dots) utils::writeClipboard(rendered_content, format = format) if (return_new) { rendered_content } else { content } } clipr/R/clipr-package.R0000644000175000017500000000105314177762460014560 0ustar nileshnilesh#' clipr: Read and Write from the System Clipboard #' #' Simple utility functions to read from and write to the Windows, OS X, and X11 #' clipboards. #' #' The basic functions [read_clip()] and [write_clip()] wrap #' platform-specific functions for writing values from R to the system #' clipboard. [read_clip_tbl()] will attempt to process the clipboard #' content like a table copied from a spreadsheet program. #' #' [clipr_available()] is useful when building packages that #' depend on clipr functionality. #' #' @docType package #' @name clipr NULL clipr/R/utils.R0000644000175000017500000000624414177762460013225 0ustar nileshnilesh.onAttach <- function(libname, pkgname) { packageStartupMessage("Welcome to clipr. See ?write_clip for advisories on writing to the clipboard in R.") } # Determine system type sys_type <- function() { return(Sys.info()["sysname"]) } #' Is the system clipboard available? #' #' Checks to see if the system clipboard is write-able/read-able. This may be #' useful if you are developing a package that relies on clipr and need to #' ensure that it will skip tests on machines (e.g. CRAN, Travis) where the #' system clipboard may not be available. #' #' @note This will automatically return `FALSE`, without even performing the #' check, if you are running in a non-interactive session. If you must call #' this non-interactively, be sure to call using #' `clipr_available(allow_non_interactive = TRUE)`, or by setting the #' environment variable `CLIPR_ALLOW=TRUE`. **Do not attempt to run #' clipr non-interactively on CRAN; this will result in a failed build!** #' #' @param \ldots Pass other options to [`write_clip()`]. Generally only used to #' pass the argument `allow_non_interactive_use = TRUE`. #' #' @return `clipr_available` returns a boolean value. #' #' @examples #' \dontrun{ #' # When using testthat: #' library(testthat) #' skip_if_not(clipr_available()) #' } #' #' @export clipr_available <- function(...) { clipr_results_check(clipr_available_handler(...)) } #' @rdname clipr_available #' #' @return Prints an informative message to the console with #' software and system configuration requirements if clipr is not available #' (invisibly returns the same string) #' #' @export dr_clipr <- function(...) { res <- clipr_available_handler(...) if (clipr_results_check(res)) { msg <- msg_clipr_available() } else { msg <- attr(res$write, which = "condition", exact = TRUE)$message } message(msg) invisible(msg) } clipr_available_handler <- function(...) { # Do not even do a check unless user has explicitly set CLIPR_ALLOW if (!interactive()) { clipr_allow <- as.logical(Sys.getenv("CLIPR_ALLOW", "FALSE")) if (!clipr_allow) { fake_write_attempt <- try(stop("CLIPR_ALLOW has not been set, so clipr will not run interactively"), silent = TRUE) return(list(write = fake_write_attempt)) } } suppressWarnings({ read_attempt <- try(read_clip(...), silent = TRUE) write_attempt <- try(write_clip(read_attempt, ...), silent = TRUE) }) return(list(read = read_attempt, write = write_attempt)) } clipr_results_check <- function(res) { if (inherits(res$write, "try-error")) return(FALSE) if (inherits(res$read, "try-error")) return(FALSE) TRUE } msg_clipr_available <- function() "clipr has read/write access to the system clipboard!" msg_no_clipboard <- function() "Clipboard on X11 requires 'xclip' (recommended) or 'xsel'; Clipboard on Wayland requires 'wl-copy' and 'wl-paste'." msg_no_display <- function() "Clipboard on X11 requires that the DISPLAY envvar be configured." msg_interactive <- function() "To run write_clip() in non-interactive mode, either call write_clip() with allow_non_interactive = TRUE, or set the environment variable CLIPR_ALLOW=TRUE" error_interactive <- function() { stop(msg_interactive()) } clipr/R/osx_clipboard.R0000644000175000017500000000321614177762460014711 0ustar nileshnilesh# Helper function to read from the OS X clipboard # Adapted from https://github.com/jennybc/reprex/blob/master/R/clipboard.R osx_read_clip <- function() { con <- pipe("pbpaste") content <- scan(con, what = character(), sep = "\n", blank.lines.skip = FALSE, quiet = TRUE) close(con) return(content) } # Helper function to write to the OS X clipboard # Adapted from https://github.com/jennybc/reprex/blob/master/R/clipboard.R osx_write_clip <- function(content, object_type, breaks, eos, return_new, ...) { .dots <- list(...) con <- pipe("pbcopy") write_nix(content, object_type, breaks, eos, return_new, con, .dots) } # The same content rendering and writing steps are used in both OS X and Linux, # just with different connection objects write_nix <- function(content, object_type, breaks, eos, return_new, con, .dots) { # If no custom line separator has been specified, use Unix's default newline # character '\n' breaks <- ifelse(is.null(breaks), '\n', breaks) # If no custom tab separator for tables has been specified, use Unix's default # tab character: '\t' .dots$sep <- ifelse(is.null(.dots$sep), '\t', .dots$sep) # Pass the object to rendering functions before writing out to the clipboard rendered_content <- render_object(content, object_type, breaks, .dots) # Suppress pipe() warning when writing an empty string with a NULL string # ending. if (identical(rendered_content, "")) { suppressWarnings(writeChar(rendered_content, con = con, eos = eos)) } else { writeChar(rendered_content, con = con, eos = eos) } close(con) if (return_new) { rendered_content } else { content } } clipr/R/read_clip_tbl.R0000644000175000017500000000276214177762460014651 0ustar nileshnilesh#' Transforms output of [read_clip()] into data frame. #' #' Transforms clipped content into a data frame by putting #' [read_clip()] output by using [read.table()]. #' #' @param x Defaults to reading from the clipboard, but can be substituted by a #' character vector already generated by [read_clip()]. #' @param \ldots Options to pass to [read.table()]. The following #' [read.table()] arguments will be passed by default, but can be #' overridden by specifying them when calling `read_clip_tbl`: \describe{ #' \item{`header`}{`TRUE`} #' \item{`sep`}{`"\t"`} #' \item{`row.names`}{`1`} #' \item{`stringsAsFactors`}{`FALSE`} #' \item{`na.strings`}{`c("NA", "")`} #' \item{`strip.white`}{`TRUE`} } #' #' @return A data frame with the contents of the clipboard. If the system #' clipboard is empty, returns `NULL` #' #' @export read_clip_tbl <- function(x = read_clip(), ...) { if (is.null(x)) return(NULL) .dots <- list(...) .dots$file <- textConnection(paste0(x, collapse = "\n")) if (is.null(.dots$header)) .dots$header <- TRUE if (is.null(.dots$row.names)) { if (substr(x[1], 1, 1) == "\t") { .dots$row.names <- 1 } else { .dots$row.names <- NULL } } if (is.null(.dots$sep)) .dots$sep <- "\t" if (is.null(.dots$stringsAsFactors)) .dots$stringsAsFactors <- FALSE if (is.null(.dots$na.strings)) .dots$na.strings <- c("NA", "") if (is.null(.dots$strip.white)) .dots$strip.white <- TRUE do.call(utils::read.table, args = .dots) } clipr/inst/0000755000175000017500000000000014204051745012474 5ustar nileshnileshclipr/inst/rstudio/0000755000175000017500000000000014177762460014201 5ustar nileshnileshclipr/inst/rstudio/addins.dcf0000644000175000017500000000045714177762460016127 0ustar nileshnileshName: Value to clipboard Description: Copies the results of a selected expression to the system clipboard Binding: clipr_result Interactive: false Name: Output to clipboard Description: Copies the console output of a selected expression to the system clipboard Binding: clipr_output Interactive: false clipr/inst/doc/0000755000175000017500000000000014204051745013241 5ustar nileshnileshclipr/inst/doc/developing-with-clipr.html0000644000175000017500000002505514204051745020352 0ustar nileshnilesh Developing with clipr

Developing with clipr

Matthew Lincoln

2022-02-18

Calling clipr safely

To check whether the system has been properly configured to allow access to the clipboard, you may run clipr_available() which will either return TRUE or FALSE. This will be particularly useful for Linux-based systems, where clipr’s functionality depends on the installation of additional software. If you wish to display system requirements and configuration messages to X11 and Wayland users, dr_clipr() will print these.

Interactive & non-interactive use

If you use clipr in your own package, you must not try to call it in non-interactive sessions, as this goes against CRAN repository policy:

Packages should not write in the user’s home filespace (including clipboards), nor anywhere else on the file system apart from the R session’s temporary directory (or during installation in the location pointed to by TMPDIR: and such usage should be cleaned up). Installing into the system’s R installation (e.g., scripts to its bin directory) is not allowed.

Limited exceptions may be allowed in interactive sessions if the package obtains confirmation from the user.

For this reason, write_clip() will error by default in non-interactive use, which includes CRAN tests.

If you want to use write_clip() non-interactively, you may either set the environment variable CLIPR_ALLOW=TRUE or call write_clip(..., allow_non_interactive = TRUE).

Testing on CRAN and CI

A few best practices will also help you responsibly test your clipr-using package on headless systems like CRAN or other testing infrastructure like Travis:

  1. Examples that will try to use read_clip() or write_clip() ought to be wrapped in \dontrun{}
  2. Tests calling clipr should be conditionally skipped, based on the output of clipr_available(). This is necessary to pass CRAN checks, as otherwise write_clip will error out.
  3. If you are using a headless system to check your package build on Linux, consult the GitHub Actions workflow for this package, which includes code for setting the DISPLAY and CLIPR_ALLOW environment variables, installing xclip and xsel, and running a pre-build script that will set up xclip/xsel to run headlessly under XVFB.

Using clipr with Shiny

clipr won’t do what you expect when you call it with Shiny.

clipr talks to the clipboard of the system that is running R. If you create a Shiny app and tell one of its functions to either read from or write to the clipboard, it can only access the clipboard of the server it is running on. R running on the remote server has no way to access the local clipboard belonging to your end user.

However, you can instruct the user’s internet browser to write to the user’s clipboard by using rclipboard.

clipr/inst/doc/developing-with-clipr.Rmd0000644000175000017500000000631614200536221020120 0ustar nileshnilesh--- title: "Developing with clipr" author: "Matthew Lincoln" date: "`r Sys.Date()`" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Developing with clipr} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` ## Calling clipr safely To check whether the system has been properly configured to allow access to the clipboard, you may run `clipr_available()` which will either return `TRUE` or `FALSE`. This will be particularly useful for Linux-based systems, where clipr's functionality depends on the installation of additional software. If you wish to display system requirements and configuration messages to X11 and Wayland users, `dr_clipr()` will print these. ## Interactive & non-interactive use If you use clipr in your own package, **you must not try to call it in non-interactive sessions**, as this goes against [CRAN repository policy](https://cran.r-project.org/web/packages/policies.html): > Packages should not write in the user’s home filespace (including clipboards), nor anywhere else on the file system apart from the R session’s temporary directory (or during installation in the location pointed to by TMPDIR: and such usage should be cleaned up). Installing into the system’s R installation (e.g., scripts to its bin directory) is not allowed. > > Limited exceptions may be allowed in interactive sessions if the package obtains confirmation from the user. For this reason, `write_clip()` will error by default in non-interactive use, which includes CRAN tests. If you want to use `write_clip()` non-interactively, you may either set the environment variable `CLIPR_ALLOW=TRUE` or call `write_clip(..., allow_non_interactive = TRUE)`. ## Testing on CRAN and CI A few best practices will also help you responsibly test your clipr-using package on headless systems like CRAN or other testing infrastructure like Travis: 1. Examples that will try to use `read_clip()` or `write_clip()` ought to be wrapped in `\dontrun{}` 2. Tests calling clipr should be conditionally skipped, based on the output of `clipr_available()`. This is necessary to pass CRAN checks, as otherwise `write_clip` will error out. 3. If you are using a headless system to check your package build on Linux, consult the [GitHub Actions workflow](https://github.com/mdlincoln/clipr/blob/main/.github/workflows/R-CMD-check.yaml) for this package, which includes code for setting the `DISPLAY` and `CLIPR_ALLOW` environment variables, installing `xclip` and `xsel`, and running a pre-build script that will set up `xclip`/`xsel` to run headlessly under XVFB. ## Using clipr with Shiny clipr won't do what you expect when you call it with Shiny. clipr talks to the clipboard of the _system that is running R_. If you create a Shiny app and tell one of its functions to either read from or write to the clipboard, it can only access the clipboard of the server it is running on. R running on the _remote_ server has no way to access the _local_ clipboard belonging to your end user. However, you can instruct the user's _internet browser_ to write to the user's clipboard by using [rclipboard](https://cran.r-project.org/package=rclipboard). clipr/inst/doc/developing-with-clipr.R0000644000175000017500000000021714204051745017600 0ustar nileshnilesh## ----setup, include = FALSE--------------------------------------------------- knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) clipr/NAMESPACE0000644000175000017500000000027514204050253012733 0ustar nileshnilesh# Generated by roxygen2: do not edit by hand export(clear_clip) export(clipr_available) export(dr_clipr) export(read_clip) export(read_clip_tbl) export(write_clip) export(write_last_clip)