The other day I was hacking away, using DrScheme as a scratchpad and a Google Spreadsheet to visualize data.
After way too many rows of data entry, it hit me - I can trivially automate copying values from DrScheme to a Google Spreadsheet. PLT-Scheme gives you access to a clipboard object that you can simply call set-clipboard-string. Using this method, I was able to write a Scheme function that injected data into the system clipboard, which I could then paste into my spreadsheet.
To smooth things out a bit, I wrote a function which takes in lists of lists, and converts them to tab-and-newline-delimited strings, that Google knows how to parse into cells.
The result is that I can now type:
;; ctc is an alias for copy-to-clipboard (ctc '((Name Rank Serial-Number) (Ben Private 123) (Shira General 456)))
and then hit Control-V to get:
Of course, if Ireally wanted to push data to a Google Spreadsheet, there are much cleaner ways to do it. But, this copy and paste hack gave me just the solution I needed.
It's also worth mentioning that I do this sort of thing all the time from the command line. There's a standard Windows command, C:\Windows\System32\clip.exe that allows you to send content to the clipboard via the shell. (In cygwin, I can do: cat /etc/passwd | tr a-z A-Z | clip).
Though, in this case, I was already poking around DrScheme so a it made more scheme to develop a PLT specific solution.
Here's the code:
#lang scheme (require scheme/gui ; used to get the-clipboard variable srfi/26 ; for cut ) ;; ;; The clipboard functions choke on null strings. So I replace ;; them with this character. Most of the time, you won't need ;; to worry about this ;; (define null-character-replacement (make-parameter "$")) (define as-string (cut format "~a" <>)) ;; ;; gridify: take in a list of lists and turn it into a tab/newline ;; delimited string, ready to be copied and pasted into a spreadsheet. ;; Also handles the case where the data isn't a lists of lists. ;; (define (gridify any) (if (list? any) (string-join (map (compose as-string (lambda (row) (if (list? row) (string-join (map as-string row) "\t") row))) any) "\n") (as-string any))) ;; ;; Copy any content to the clipboard as a string. Note: we call (gridify any) ;; to make the data more clipboard friendly ;; ;; As a convenience, return the data sent in. Useful to allow calling ;; this function in a debugging context. ;; (define (copy-to-clipboard any) (let ([scrubbed (regexp-replace* "[\0]" (gridify any) (null-character-replacement))]) (send the-clipboard set-clipboard-string scrubbed 0) any)) ;; ;; provide a compact alias for (copy-to-clipboard ...) ;; (define ctc copy-to-clipboard) (provide/contract [copy-to-clipboard (any/c . -> . any/c)] [ctc (any/c . -> . any/c)])
Just a side note in case someone is interested:
ReplyDeleteOn OS X the clipboard copying command is pbcopy and you can use it like this:
(require scheme/system)
(define (pbcopy data)
(define-values (stdout stdin pid stderr proc) (apply values (process "pbcopy")))
(close-input-port stdout) (close-input-port stderr)
(display data stdin)
(close-output-port stdin)
(proc 'wait))
I've gone on a jag recently, replacing all my uses of "cut" with "curry", which I just think is slicker. (And it's in "scheme", so you don't have to require anything to get it).
ReplyDeleteSo
(require srfi/26)
(define as-string (cut format "~a" <>))
becomes
(define as-string (curry format "~a"))
JPC - Thanks for the OS-X tip.
ReplyDeleteOffyb1 - plt-scheme never ceases to amaze...look at that, they've added (curry ...). I'll definitely start looking for ways to use it. Though, I do like the explicit nature of (cut ...) which allows for invocations like:
(map (cut format <> 100 'x)
'("!~a!~a~" ">~a!~a<"))
PLT-Sheme also offers a a curried notation of (define ...) which is cool, though I haven't found an ideal use case for it yet.