Skip to content

Commit

Permalink
bells and whistles and documentation for util::Clipboard; this adds i…
Browse files Browse the repository at this point in the history
…nsight into different mimetypes that are supported, as well as the ability to paste from very specific mimetypes
  • Loading branch information
jurgenvinju committed Apr 8, 2024
1 parent 8ee828c commit e8aa6b7
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 3 deletions.
47 changes: 45 additions & 2 deletions src/org/rascalmpl/library/util/Clipboard.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
import java.io.Reader;
import java.util.Arrays;

import org.rascalmpl.debug.IRascalMonitor;
import org.rascalmpl.exceptions.RuntimeExceptionFactory;
import org.rascalmpl.library.Prelude;

import io.usethesource.vallang.ISet;
import io.usethesource.vallang.IString;
import io.usethesource.vallang.IValueFactory;

Expand All @@ -26,8 +33,10 @@ public void copy(IString arg) {

public IString paste() {
try {
if (cp.isDataFlavorAvailable(DataFlavor.stringFlavor)) {
return vf.string(cp.getData(DataFlavor.stringFlavor).toString());
DataFlavor flavor = DataFlavor.getTextPlainUnicodeFlavor();

try (Reader data = flavor.getReaderForText(cp.getContents(null))) {
return vf.string(Prelude.consumeInputStream(data));
}
}
catch (UnsupportedFlavorException | IOException e) {
Expand All @@ -36,4 +45,38 @@ public IString paste() {

return vf.string("");
}

public IString paste(IString mimetype) {
try {
DataFlavor flavor = new DataFlavor(mimetype.getValue());

try (Reader data = flavor.getReaderForText(cp.getContents(null))) {
return vf.string(Prelude.consumeInputStream(data));
}
}
catch (ClassNotFoundException e) {
throw RuntimeExceptionFactory.illegalArgument(vf.string("Unsupported clipboard mimetype: " + mimetype));
}
catch (UnsupportedFlavorException e) {
throw RuntimeExceptionFactory.illegalArgument(vf.string("Unsupported clipboard mimetype: " + e.getMessage()));
}
catch (IOException e) {
throw RuntimeExceptionFactory.io(e.getMessage());
}
}

public ISet availableTextMimetypes() {
return Arrays.stream(cp.getAvailableDataFlavors())
.filter(flavor -> flavor.isFlavorTextType())
.map(flavor -> vf.tuple(vf.string(flavor.getHumanPresentableName()), vf.string(flavor.getMimeType())))
.collect(vf.setWriter());
}

public ISet availableTextMimetypesFor(IString shortMimetype) {
return Arrays.stream(cp.getAvailableDataFlavors())
.filter(flavor -> flavor.isFlavorTextType())
.filter(flavor -> flavor.isMimeTypeEqual(shortMimetype.getValue()))
.map(flavor -> vf.tuple(vf.string(flavor.getHumanPresentableName()), vf.string(flavor.getMimeType())))
.collect(vf.setWriter());
}
}
62 changes: 61 additions & 1 deletion src/org/rascalmpl/library/util/Clipboard.rsc
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
@synopsis{Programmatic access to the native system clipboard (copy and paste), with some handy (de)-escaping features.}
module util::Clipboard

import Exception;

@javaClass{org.rascalmpl.library.util.Clipboard}
@synopsis{If there is textual content in the current clipboard, then return it as a string.}
@description{
Expand All @@ -12,8 +14,13 @@ then paste always returns the empty string.
}
@pitfalls{
* the first time paste or copy are called the UI toolkit must boot up; it can take a few seconds.
* paste provides the content of the current clipboard, where other processes can always put new content int.
The `paste` function is thread-safe, it will provide the contents of the clipboard as it was at a certain
time, but it does not have to be the latest version.
}
@benefits{
* past never throws exceptions, it just returns "" if something fails and prints an exception. If you
need a less robust version look at `past(str mimetype)` below.
* copy/paste allow for interesting and useful user interactions, especially while experimenting on the REPL.
* paste encodes and escapes all kinds of wild characters automatically.
}
Expand All @@ -31,9 +38,62 @@ then copy always has no effect.
}
@pitfalls{
* the first time paste or copy are called the UI toolkit must boot up; it can take a few seconds.
* if another application copies something to the clipboard, or content can be overwritten. There
is always a race for the clipboard, but this function is thread-safe. Right after its execution the
content of the clipboard is guaranteed to contain `content`.
}
@benefits{
* copy/paste allow for interesting and useful user interactions, especially while experimenting on the REPL.
* copy transfers the pure content of the string, no quotes or escapes.
}
java void copy(str content);
java void copy(str content);

@javaClass{org.rascalmpl.library.util.Clipboard}
@synopsis{Lists the available mimetypes on the current clipboard, restricted to the ones we can serialize to string.}
@description{
The clipboard can contain many kinds of textual data. With this query you retrieve all the supported mimetypes that we can
safely and correctly serialize to a string.

Most of the mimetypes contain the exact charset encoding parameter, such that we do not have to worry about that here.
Using `paste(str mimetype)` those details will be taken care off. However if you call `paste` with an unavailable
encoding, there will be an appropriate exception.

If this function returns `{}` then there is nothing on the clipboard that can be serialized to a string.
}
@pitfalls{
* after calling this function, the user may have selected another content for the clipboard. There is always a race
for the clipboard. This function is thread-friendly, however and will not crash but just provide outdated information.
}
java rel[str humanReadable, str fullMimetype] availableTextMimetypes();

@javaClass{org.rascalmpl.library.util.Clipboard}
@synopsis{Lists the available mimetypes on the current clipboard, restricted to the ones we can serialize to string and that start with `shortMimetype`.}
@description{
The clipboard can contain many kinds of textual data. With this query you retrieve all the exact supported mimetypes
for, for example, `text/html`. The function will fill in the specific charset and implementation class parameters of
the mimetype, for all supported formats.

Most of the mimetypes contain the exact charset encoding parameter, such that we do not have to worry about that here.
Using `paste(str mimetype)` those details will be taken care off. However if you call `paste` with an unavailable
encoding, there will be an appropriate exception.

If this function returns `{}` then there is nothing on the clipboard that can be serialized to a string and matches
the `shortMimetype`.
}
@pitfalls{
* after calling this function, the user may have selected another content for the clipboard. There is always a race
for the clipboard. This function is thread-friendly, however and will not crash but just provide outdated information.
}
java rel[str humanReadable, str fullMimetype] availableTextMimetypesFor(str shortMimetype);

@javaClass{org.rascalmpl.library.util.Clipboard}
@synsopsis{Serializes the current contents of the clipboard that matches the given mimetype to a string}
@description{
This only works for ((availableTextMimetypes)), otherwise an exception is thrown.

This function behaves as `paste()`, but it can serialize all kinds of other data, as long as the internal
data flavors for the given mimetypes support textual serialization. In principle these are the mimetypes
that list a charset parameter, but there are also some heuristics that enlarge the set a bit. The supported
mimetypes are always listed in ((availableTextMimetypes)).
}
java str paste(str mimetype) throws IO, IllegalArgument;

0 comments on commit e8aa6b7

Please sign in to comment.