You should be automatically redirected . If not, visit
and update your bookmarks.


Snip, Snip, Snip!

With two or three posts in various states of undress — unable to find the shoes and socks for this one, or a shirt and tie for that one (basically, they're not decent) — I've decided to start my third or fourth! The nakedness of the earlier posts mostly hinges on the code, or lack thereof. I never thought the writing would be the easy part!

So with this in mind, I decided to write about some already useful and working code — but what code to write about?

How about a nice game of Global Thermo . . . no, no, no. How about a nice post about the clipboard? This is going to be Mac-OS-X–specific (sorry, users of lesser OSes ;-), but those parts are few, and the capable newLISP hackers are many (relatively).

Let's begin with the OS-specific functions copy and paste within the clipboard context:

(context 'clipboard)
    (define (copy)
        ;! string-to-copy
        ;& copies string-to-copy to the system clipboard
        ;= number of characters copied
        (let (s (args 0))
        (exec (string {echo '} s {' | pbcopy}))
        (length s))) 

    (define (paste)
        ;= string of the clipboard contents
        (join (exec {pbpaste}) "\n"))
(context MAIN)

We define a context with two functions. The first, copy, takes a string, puts it on the system clipboard, and returns the number of characters copied. The Mac-only part is pbcopy, a shell command that takes standard input and writes it to the clipboard. echo and | (pipe) are found on most shells (I think) and are BSP (Beyond the Scope of this Post). Also, I totally condemn that last expression as being too much like "Beyond the Scope of this Book," which always struck me as a way for the author to get out of explaining something difficult or doing research (like me, right now).

The second function, paste, returns the text on the clipboard as a string. pbpaste is the Mac-specific command in this function and is executed, naturally enough, with the exec function. This function returns a list of the lines on the clipboard, so we need to stitch them together with a string containing a single newline by using join.

It might be a little cumbersome to call clipboard:copy and clipboard:paste all of the time, so let's define a global function called clip, which serves as an interface to both functions. We do this by making the function optionally take a string or nothing at all. The string is what we want to copy, but if we call clip by itself, we get the string of the clipboard contents. Enough introductions! Bring on the code:

(define (clip)
    ;? nothing
    ;= string of the clipboard contents
    ;? string-to-copy
    ;= number of characters copied
    (if (args 0)
        (clipboard:copy (args 0))

(global 'clip)

clip is really just a convenience function, but I find it handy. Okay, we're to that part. Do you know which part? The playing part, of course!

> (clip "She clipped coupons continuously, forgetting to buy their intended purposes.")
> (string (0 32 (clip)) " . . .")
"She clipped coupons continuously . . ."
> ;; that was over too soon! wait, i know . . .
> (begin (clip (0 32 (clip))) (string (clip)  ". Snip, snip, snip!"))
"She clipped coupons continuously. Snip, snip snip!"
> _

I see now that if clip returned the text copied, instead of the number of characters, the last line would become:

(string (clip (0 32 (clip))) ". Snip, snip, snip!")

That is a lot more elegant, but I still like that clipboard:copy returns the character-copied count. I know what I just copied to the clipboard — tell me something I don't know. Besides, it works just like clipboard:copy's hero, write-file :-)

That would seem to be it, right? Well, how about some documentation for our little global function. At one point in my distant newLISP past (two months), I thought the best way to do this was to . . . well, you can read about it here. There's a new way, brother (Clockwork Orange), and the new, new way (At least for me. Right now. Till Lutz patiently explains a better way to do it ;-) is this:

(define (docs:clip) (show {clip
    (clip str)  ; places str onto system clipboard
    (clip)  ; returns system clipboard as text


    > (clip "Hello, world!")
    > ;; the length of the copied text
    > _}))

Right off the bat, I want to call attention to the user-function show. This is not part of newLISP and simply prints the string argument silently to stdout (between down and up arrows), finishing with a normal prompt (necessary because I used the silent function, which even tells the ">" to shut up). Better just to show you what it looks like:

> (docs:clip)
    (clip str)  ; places str onto system clipboard
    (clip)  ; returns system clipboard as text


    > (clip "Hello, world!")
    > ;; the length of the text clipped
    > _
> _

The other cool thing to do is to tie this in with Lutz's help or cormullion's ?? macros. If the passed function is not a built-in, the macro can be augmented to call the function inside docs.

The real point here is that, rather than using things in ways they weren't meant to be used (forcing contexts to masquerade as normal functions, just to make those functions look like they also had functions — function functions), I'm putting contexts to work in a more natural and obvious way. Where do you put your docs? Why, in the docs context, obviously. And naturally, too. :-)

Is this the end? Yes. Bye now!

m i c h a e l

P.S. Anyone interested, let me know, and I will post the source for the show function, cormullion's or Lutz's macros, or any other bits to the comments section. But since my only two readers are you two, anyway, you probably already have them ;-)

;! required
;? optional
;& side effect
;= result
;-) wink

U P D A T E : The following code snippets were requested, and since Blogger will not let me format the code (in a sane way) in the comments section, I'm posting here. Here is the code for show:

(define (show)
    ;? nothing
    ;& prints MAIN's symbol = value pairs
    ;? context
    ;& prints context's symbol = value pairs
    ;? function
    ;& prints function's definition
    (if (empty? (args))
        (show MAIN)
            (dolist (arg (args))
                (println utf:down-arrow)
                (if (context? arg)
                        (println arg ":")
                        (dotree (s arg) 
                                (print (name s) " = " (eval s) "\n"))))
                    (silent (println arg))))
            (print utf:up-arrow "\n> "))))

(global 'show)

And here is the code for the utf context that show depends on:

(context 'utf)
        'eacute "´"
        'emdash "—"
        'dagger "†"
        'down-arrow (char 8595)
        'up-arrow (char 8593))
(context MAIN)

(I'm using this on a UTF-8 enabled setup.) Obviously, you can substitute the char calls directly into the function and bypass the utf context entirely. Next is cormullion's ?? macro:

(define-macro (?? func)
    "(?? println) => displays syntax for func "
    (if (primitive? (eval func))
            (set 'file (open "/usr/share/newlisp/doc/newlisp_manual.html" "read"))
            (search file (string {<a NAME="} (name func) {">} ) )
            (read-buffer file 'buff 500 )
            (replace  "<.+>" buff "" 512 )
            (replace ">" buff ">")
            (replace "<" buff "<")
            (println buff "...")
            (close file))))

(global '??)

Last, but not least, Lutz's help macro:

(define-macro (help func) 
    (if (primitive? (eval func)) 
        (!  (format "open" (name func))) 
        (format "%s is not a built-in function" (name func))))

(global 'help)

Lutz also posted this to use with lynx:

(define-macro (help func) 
    (if (primitive? (eval func)) 
        (!  (format "lynx /usr/share/newlisp/doc/newlisp_manual.html#%s" (name func))) 
        (format "%s is not a built-in function" (name func))))

That should do it!



At 10:26, Anonymous Anonymous said...

Hi Michael,
Could you please post more about the show function and Lutzs and cormullians macros. Thanks.

At 15:04, Anonymous cormullion said...

I must add that my function was a humble first attempt at modifying a macro supplied by someone else, and it could do with much fine-tuning before it worked... For example, it needs to be examined for the way it works with function names that have '-' in them...

At 15:12, Anonymous m i c h a e l said...

Hi anonymous,

I'd be happy to! Just see the end of the original post for the code you requested.

Thanks for reading!

m i c h a e l


Post a Comment

Links to this post:

Create a Link

<< Home