Git Product home page Git Product logo

clipr's Introduction

clipr's People

Contributors

cderv avatar jennybc avatar krivit avatar lmmx avatar mdlincoln avatar milesmcbain avatar qulogic avatar yutannihilation avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

clipr's Issues

Windows clipbaord functions need `format = 13` on R >= 4.2

You know, the upcoming R 4.2 for Windows will support UTF-8 as native encoding.

c.f. https://blog.r-project.org/2021/12/07/upcoming-changes-in-r-4.2-on-windows/index.html

While this itself is a very great change, this causes a possible mismatch between the system's encoding and R's encoding. Currently, the default of format argument is 1, which means (from ?clipboard):

CF_TEXT	1	Text in the machine's locale

but R uses UTF-8. Because of this gap, the texts will get garbled like this: tidyverse/reprex#406

To avoid the problem, we need to specify 13, which means unicode (UTF-16).

CF_UNICODETEXT	13	Text in Unicode (UCS-2)

By choosing this, the R's connection API will converts the text from / to R strings properly (I don't understand the mechanism, but it seems to work so. c.f., https://bugs.r-project.org/show_bug.cgi?id=18267). Actually, this is the recommended setting by ?clipboard:

It is recommended to use Unicode text instead of text to avoid interoperability problems. (Note that R 4.2 and newer on recent systems uses UTF-8 as the native encoding but the machine's locale uses a different encoding.)

Use native (C) apis

This package would be even more awesome (and more elegant) if instead of using command-line tools, you used the native API on each platform. This is probably a bit fiddly, but I but there are three people in the R community who each know how to do it on one platform.

cc @kevinushey, hint hint...

Can clipr_available() start a process?

Since clipr_available() calls clipr_available_handler() and that tries to both read and write the clipboard, don't you think a call to clipr_available() could still create problems at CRAN, i.e. it could still tickle some process and leave it running?

Perhaps clipr_available_handler() should look more like this:

clipr_available_handler <- function(...) {
  if (!interactive()) {
    clipr_allow <- as.logical(Sys.getenv("CLIPR_ALLOW", "FALSE"))
    if (!clipr_allow) {
      return(FALSE)
    }
  }
  suppressWarnings({
    read_attempt <- try(read_clip(...), silent = TRUE)
    write_attempt <- try(write_clip(read_attempt, ...), silent = TRUE)
  })
  list(read = read_attempt, write = write_attempt)
}

Interactive use and copying graphs

First, thank you for this awesome package!

Before I discovered this package recently, I have been maintaining a package that has similar functionalities within my company for 3 years. It is not as thorough as clipr and it only works on Windows. However, there are some features in that package that are really handy. I'd love to know what you think about them and if you'd consider adding them to clipr. I'd be happy to make some pull requests.

  1. Use .Last.value as the default value for copying.

    • It's great for interactive use. When an object is printed in the console, just type clipr::write_clip() in console and the object will be in the clipboard, instead of typing clipr::write_clip(.Last.value) (I found it hard to type .Last.value right).
    • I found that what users want to copy out is usually end results that they want to show others. In the R script, this is not necessarily an object that they want to save and reuse. And it's hard to come up with object names in the first place. In the current setting, the object need to be assigned and passed to content.
    • This is a feature that can be added to write_clip() directly or have a wrapper just for interactive use.
  2. Now write_clip() returns either rendered_content or content based on return_new. As discussed here in Advanced R, is it possible for write_clip() to always return the content invisibly?

    • For a function that triggers a side effect, it's nice to be able to predict what it returns.
    • Another reason is that the calls can be chained. See below for an example. Right now, the second call with col.names = FALSE won't work. The second call will take the rendered characters from the first call (with column names) and copy it. It will require head(mtcars) to be saved and passed explicitly, or head(mtcars) to be run and copied again.
> head(mtcars)
                   mpg cyl disp  hp drat    wt  qsec vs am gear carb
Mazda RX4         21.0   6  160 110 3.90 2.620 16.46  0  1    4    4
Mazda RX4 Wag     21.0   6  160 110 3.90 2.875 17.02  0  1    4    4
Datsun 710        22.8   4  108  93 3.85 2.320 18.61  1  1    4    1
Hornet 4 Drive    21.4   6  258 110 3.08 3.215 19.44  1  0    3    1
Hornet Sportabout 18.7   8  360 175 3.15 3.440 17.02  0  0    3    2
Valiant           18.1   6  225 105 2.76 3.460 20.22  1  0    3    1
> clipr::write_clip(.Last.value)
> clipr::write_clip(.Last.value, col.names = FALSE)

If it returns content by default, and combined with .Last.value as default input, it can be used like this:

> head(mtcars)
                   mpg cyl disp  hp drat    wt  qsec vs am gear carb
Mazda RX4         21.0   6  160 110 3.90 2.620 16.46  0  1    4    4
Mazda RX4 Wag     21.0   6  160 110 3.90 2.875 17.02  0  1    4    4
Datsun 710        22.8   4  108  93 3.85 2.320 18.61  1  1    4    1
Hornet 4 Drive    21.4   6  258 110 3.08 3.215 19.44  1  0    3    1
Hornet Sportabout 18.7   8  360 175 3.15 3.440 17.02  0  0    3    2
Valiant           18.1   6  225 105 2.76 3.460 20.22  1  0    3    1
> clipr::write_clip()  # paste it out and check results
> clipr::write_clip(col.names = FALSE)  # not satisfy with results, call again with additional settings
  1. Copying plots? Which I think rstudioapi already have the ability to do but it's not programmatic.
  2. There may be other concerns about this, but providing tibble objects when reading tables? So when users paste in a long table, they won't see it taken over the whole screen as long as tibble is loaded..

Right now I have wrapper functions built on top of clipr for all these features (except for 3). Let me know if you'd consider having these as part of clipr. Thanks.

Yuchen

Running xsel hangs on RStudio

Running system2("xsel", "--clipboard") causes my machine to hang (could be an i3 thing) when using RStudio console.

Changing

has_xsel <- function() has_util(c("xsel", "--clipboard"))

to

#linux_clipboard.R
has_xsel <- function() has_util(c("xsel", "--clipboard", "--output"))

Solves my issues.

The documentation suggests that this is the correct thing to do. It states that the behaviour depends on whether input / output are ttys:

   By default, this program outputs the selection without modification if both standard input  and
   standard  output  are  terminals (ttys). Otherwise, the current selection is output if standard
   output is not a terminal (tty), and the selection is set from standard input if standard  input
   is not a terminal (tty). If any input or output options are given then the program behaves only
   in the requested mode.
   If both input and output is required then the previous selection is  output  before  being  re‐
   placed by the contents of standard input.

Process hygiene on linux

I'm just parking this here for the future, so feel free to close if it bothers you to leave it open. But, as you know, on linux, the use of clipr launches but does not close a process (via xsel and/or xclip). Which has caused trouble on CRAN with packages that actually exercise clipr code via example, test, or vignette. Everyone's current solution seems to be shutting down or removing all such code from CRAN. That is expedient and necessary, but doesn't really seem like a solution that leads to software quality.

Food for future thought, via @kevinushey

IIUC the issue with xclip is that it works by forking itself into a background process, and then hangs around to service clipboard requests from the user. Since it forks itself to the background it never gets closed even if the parent process is closed.

In X11 (if I understand correctly) there isn't really a concept of 'writing to the clipboard', it's more that 'there are processes hanging around that service read / write requests to the clipboard'. So once xclip dies it can no longer provide a clipboard selection; hence why it wants to stick around.

I think the fix here for clipr would be to explicitly kill the xclip / xsel processes when the R session is closed / when its namespace is unloaded or something like that. There is probably a clean way of managing the process lifetime using processx or similar.

Consider renaming warn_interactive()

I strongly associate "warn" with warning() and therefore find it non-obvious that warn_interactive() throws an error:

warn_interactive <- function() {
  stop(msg_interactive())
}

Would you considering using "stop" or "abort" in this name, instead of "warn"?

I know this is just an internal function, but I'm reading things carefully right now, to rework some stuff in reprex. I want to make sure I really understand when clipr can and cannot touch the system clipboard.

ending xclip/xsel processes

The honorable BDR has raised an issue with the pending CRAN submission:

It leaves xclip processes running, contrary to the CRAN policies.

I'm not sure if this is avoidable. From the xclip docs:

The default action is to silently wait in the background for X selection requests (pastes) until another X application places data in the clipboard, at which point xclip exits silently. You can use the -verbose option to see if and when xclip actually receives selection requests from other X applications.

And the xsel docs

There is no X selection buffer. The selection mechanism in X11 is an interclient communication mediated by the X server each time any program wishes to know the selection contents, eg. to perform a middle mouse button paste. In order to implement modification of the selection(s) (in input, keep and exchange modes) this program detaches from the terminal, spawning a child process to supply the new selection(s) on demand. This child exits immediately when any other program takes over the selection(s), eg. when the user next selects some text in a terminal window or by running xsel -c.

@lmmx Am I right in thinking that there is no way to kill either of these processes without essentially wiping the clipboard?

Warning when trying to write non-character objects?

And should we try to handle conversion? My instinct is no - deciding how to format the printing of an object should be left to the user, this package should just handle moving that text to the clipboard.

NA input

A single NA causes an error as input. Two NAs do not.

clipr::write_clip(NA_character_)
#> Error in writeChar(rendered_content, con = con, eos = eos): invalid 'nchars' argument
clipr::write_clip(c(NA_character_, NA_character_))
clipr::read_clip()
#> [1] NA NA

Table-like objects should be flattened with write.table()

At the moment, passing in a data frame results in a less-that desirable output:

library(clipr)

tbl <- data.frame(a = c(1, 2, 3), b = c(4, 5, 6))
write_clip(tbl)

#> c(1, 2, 3)
#> c(4, 5, 6)

It would be better to default to write.table() somehow when a data frame is passed in. One could use the sep = "\t" option to allow easy pasting into Excel et al.

Read / write images

From my testing it seems that clipr only works with text data. Would it be possible for it to work on images too?

I implemented something like that for my spindler package (code here only tested the linux part) and realised that maybe it would be coold to have it on clipr.

Response to CRAN

cc @hrbrmstr @MilesMcBain

Bob, Miles, and I all got this email from Prof Brian Ripley recently:

CRAN packages leaving processes running
which is disallowed by the CRAN policy.

Packages

datapasta reprex splashr

leave instances of xsel running on Fedora Linux and xclip (x2) on Solaris. And that means that make-based package checking thinks the check has not finished until the rogue processes are killed manually, so is a considerable nuisance (but because 3 packages are involved, it has taken quite a while to isolate the cause, which is invoking clipr::write_clip (which clipr itself does not currently do).

vegalite contains similar code but does not exercise it.

Beyond this the CRAN team have discussed package access to the user's clipboard and feel that it falls in to the same category as access to the file system, that is should only be done in interactive sessions with the user's permission (especially but not only write access).

Please correct ASAP and before Jan 24 to safely retain the package on CRAN.

I'm not working on this yet but we should coordinate our response. It is possible we might need/want something in clipr itself re: checking for interactive session and not being in tests. Regardless, it probably makes sense to discuss here.

xclip erroring unexpectedly

  > test_check("clipr")
  1. Failure: clipr_available fails when DISPLAY is not configured; succeeds when it is (@test_render.R#12) 
  `is_clipr_available` isn't true.
  
  
  testthat results ================================================================
  OK: 1 SKIPPED: 11 FAILED: 1
  1. Failure: clipr_available fails when DISPLAY is not configured; succeeds when it is (@test_render.R#12) 
  
  Error: testthat unit tests failed
  Execution halted

more extensive tests on travis

I am using clipr in reprex and had a bit of a struggle getting things set up on travis (surprise ha). But I eventually prevailed.

I learned a couple things that you might be able to use to test clipr more extensively on travis.

Here's my .travis.yml and everything beyond the minimum is related to clipr.

https://github.com/jennybc/reprex/blob/master/.travis.yml

First, since xclip is whitelisted, you can install it and still leave sudo: false, so you stay with the container-based infrastructure and caching.

Second, you have to set the DISPLAY environment variable to use the clipboard. Somehow your tests pass (?) but mine certainly did not until I sorted this out.

Thanks again for making clipr! I am happy I can use this for the clipboard stuff 😄.

Properly test clipr_available()

After putting much effort into getting clipr to test properly on Travis (#14), there's still the issue of correctly testing whether clipr_available() actually does the job properly. Since we're using a build matrix on Travis, it's possible to explicitly test this function when we know that clipr ought to work (when DISPLAY=:90.0) and when it ought to fail (DISPLAY="").

This is the code I'm currently trying:

test_that("clipr_available fails when DISPLAY is not configured; 
succeeds when it is", {
  # Only run this test on Travis
  skip_if_not(identical(Sys.getenv("TRAVIS"), "true"))
  if (identical(Sys.getenv("DISPLAY"), ""))
    expect_false(clipr_available())
  if (identical(Sys.getenv("DISPLAY"), ":90.0"))
    expect_true(clipr_available())
})

Unfortunately, this test appears to fail when it ought to pass, clipr_available() returning FALSE when the Travis environment ought to be properly configured. Oddly, it fails even when incorporating the fix from #20

Be just as careful with read_clip() as write_clip()

I suspect that read_clip() should have the same safeguards vis-a-vis CRAN-proofing as write_clip(). So it should not even think about touching the clipboard in a non-interactive session, unless CLIPR_ALLOW is TRUE.

Copying and pasting tables into word/google docs/email

I'm struggling with getting tables I've copied using clipr to paste into external places. I often need to do a quick share of a table into an email or to a word or google document.

When I do this with a table and paste it into the things mentioned above, the spacing is all messed up when it goes to a plain text editor (ie Spark, my email client) or to an editor that has table functionality (MS Word / Google Docs).

Is there a way to do an easy copy paste that preserves the table spacing for plain text (ie has spaces to make sure the columns line up in raw text)? Or formats as a table in word/gdocs where it ends up in an actual table with cells?

I'm on OS X btw.

Default `col.names` FALSE when content is matrix

Is there any reason that col.names defaults to FALSE when writing a matrix to the clipboard? There's an explicit check for this in clipr:::table_str:

    if (is.matrix(content)) 
        .dots$col.names <- ifelse(is.null(.dots$col.names), FALSE, 
            .dots$col.names)

I had been working around this by coercing to a data.frame first, though I see I can now just pass col.names = TRUE to clipr::write_clip.

It's presumably too late to change the default, since it may break people's existing code, but it would useful if the default could be set using an option, particularly since this is almost always something that will be used interactively.

Issue with Wayland?

On Linux, no issues with X11, but when running with Wayland, I get the following error:

"Clipboard on X11 requires that the DISPLAY envvar be configured."

ANy work-around?

Read / Write HTML

Particularly useful in conjuction with knitr::kable(format = "html") to copy a HTML table to clipboard, e.g. for pasting into a mail.

For linux this can be done by specifying -target text/html in the xclip pipe (can be abbreviated to -t)
Windows appears to need a bit of boilerplate as described here
Mac seems do have a different type of boilerplate, seen here

Issue in unicode/UTF-8 handling

I've been using clipr::write_clip() for a while. It's been very helpful in transfering data from a data frame to Excel, for example.

However, clipr::write_clip() is unable to copy data in UTF-8/unicode. Changing the locale using Sys.setlocale() doesn't help, other than turning the unicode text into gibberish when set to the corresponding locale, and the text is copied as unicode when set to English. The console behaves in the opposite manner - it displays the correct foreign text when Sys.setlocale() is set to the correct locale, whereas it displays unicode when set to English.

Other functions such as write.xlsx2() work as expected. I have not found a way to make write_clip() work in this situation. Suggestions are most welcome in terms of how to convert the data frame before using write_clip. writeClipboard(format = 13) also works, but formatting is all messed up for tables.

write_clip should support copy to HTML Format

Hi @mdlincoln ,

First of all, thank you for this cross-platform solution, I think it's great. I wanted to ask if it would be possible to add a parameter to the write_clip that allows to format tables in HTML format in the clipboard:
HTML Format

This would allow to have a multi-software interaction between R, Powerpoint, Word, and most other HTML supporting platforms.

I found this, which might be related cross-platform solutions and pandoc solution:

windows only:

Thank you!

CRAN having problems on Fedora

BDR email below (FYI @jennybc @MilesMcBain I'll be looking into it this week)

The CRAN policy says

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

clipr contains a function write_clip() which does not comply. It is
exercised in its tests and despite the claim on its help page, on Fedora
leaves an xsel process running which blocks the check run from
completing. As a result we have to use --no-tests, and every time some
other package uses write_clip it too blocks the check run.

To comply with the policy, please ensure that write_clip() obtains the
user's permission and never runs in a non-interactive session. And
modify your help and tests.

Packages which violate the CRAN policy are normally removed immediately.
As several packages depend on clipr we will give you until Jan 22 to
correct this.

--
Brian D. Ripley, [email protected]
Emeritus Professor of Applied Statistics, University of Oxford

clipr not available when using tests

Hi there, and thank you for the great package.

I am developing some test for my package and I can not get it working with clipr.

image
It seems that clipr::clipr_available(allow_non_interactive = TRUE) always returns FALSE so the code is never evaluated by covr. I have read the documentation and Googled a bit, but could not find a solution.

Thank you in advance,
Phil

Warning when writing "" to clipboard

I don't understand why this gives a warning.

clipr::write_clip("")
#> Warning: problem writing to connection

Because it does seem to clear the clipboard?

Write files to clipboard?

I'd like to be able to write files to clipboard, like what happens if you right click on a file and click copy. Perhaps there could be a function clipr::write_clip_file() or something like that where you pass the path to the file as an argument?
Loving the package, thanks for your great work.
Rory

using temp files on windows?

Using capture.output with large objects can be really slow. Doing some console testing, number of rows is non-linear in its time-to-encode:

system.time( zz <- capture.output(write.table(data.frame(a=1:10000), sep="\t")) )
#    user  system elapsed 
#     0.2     0.0     0.2 
system.time( zz <- capture.output(write.table(data.frame(a=1:20000), sep="\t")) )
#    user  system elapsed 
#    0.84    0.00    0.84 
system.time( zz <- capture.output(write.table(data.frame(a=1:30000), sep="\t")) )
#    user  system elapsed 
#    2.08    0.00    2.08 
system.time( zz <- capture.output(write.table(data.frame(a=1:40000), sep="\t")) )
#    user  system elapsed 
#    4.20    0.00    4.21 
system.time( zz <- capture.output(write.table(data.frame(a=1:50000), sep="\t")) )
#    user  system elapsed 
#    7.70    0.02    7.75 

Whereas using temporary files is quite a bit faster:

tf <- "foo.txt"
system.time( {
  write.table(data.frame(a=1:10000), sep = "\t", file = tf)
  zz <- paste(readLines(tf), collapse = "\r\n")
  writeClipboard(zz)
})
#    user  system elapsed 
#    0.03    0.00    0.03 
system.time( {
  write.table(data.frame(a=1:50000), sep = "\t", file = tf)
  zz <- paste(readLines(tf), collapse = "\r\n")
  writeClipboard(zz)
})
#    user  system elapsed 
#    0.13    0.00    0.13 

And pasting into Excel works as expected in a fraction of the time.

Even going to the extreme row-count of Excel 2013/2016 (allowing for a header row):

system.time( {
  write.table(data.frame(a=1:1048575), sep = "\t", file = tf)
  zz <- paste(readLines(tf), collapse = "\r\n")
  writeClipboard(zz)
})
#    user  system elapsed 
#    3.72    0.08    3.89 

(I don't want to try that with capture.output, though by its exponential progression I imagine it would take on the order of 700 seconds if it completed at all.)

BTW: I'm testing this on R-3.2.5 on win10_64, so I don't know if or how much impact this would have on other architectures.

Need an extra column name when row.names=TRUE

I'm on Windows 7, if it matters. When I have an object that I write to the clipboard with row.names=TRUE, the column names need an extra delimiter inserted before the first column name. Otherwise the column names end up shifted left one column when pasting into Excel.

dat <- matrix(1:4, 2)
rownames(dat) <- c('a','b')
colnames(dat) <- c('c','d')
dat
library(clipr)
clipr::write_clip(dat, row.names=TRUE, col.names=TRUE, sep=",")
# paste content below
c,d
a,1,3
b,2,4

The desired result should look like this (with an extra delimeter in front of "c"):

,c,d
a,1,3
b,2,4

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.