words - some interesting utilities for text in emacs

| categories: emacs | tags:

Recently I posted about an org-link with a menu. Here I extend the idea to a command that will do approximately the same thing. The idea is a command called words that will be run interactively. It will grab the word at point, or operate on a selected region, and then offer a menu to lookup the definition, find it in a thesaurus, google it, etc… We structure the code so you can add functions to it later, without directly modifying this code. The only limitation of this code is that the functions must have a signature with no arguments. That does not seem terribly limiting, as we can check for a region, and use it if we want (see the words-google function).

(defun words-dictionary ()
  (interactive)
  (browse-url
   (format
    "http://dictionary.reference.com/browse/%s?s=t"
    (thing-at-point 'word))))

(defun words-thesaurus ()
  (interactive)
  (browse-url
   (format
    "http://www.thesaurus.com/browse/%s"
    (thing-at-point 'word))))

(defun words-google ()
  (interactive)  
  (browse-url
   (format
    "http://www.google.com/search?q=%s"
    (if (region-active-p)
        (url-hexify-string (buffer-substring (region-beginning)
                                             (region-end)))
      (thing-at-point 'word)))))


(defvar words-funcs '()
 "functions to run in `words'. Each entry is a list of (key menu-name function).")

(setq words-funcs
  '(("d" "ictionary" words-dictionary)
    ("t" "hesaurus" words-thesaurus)
    ("g" "oogle" words-google)))
 

(defun words ()
  (interactive)
   (message
   (concat
    (mapconcat
     (lambda (tup)
       (concat "[" (elt tup 0) "]"
               (elt tup 1) " "))
     words-funcs "") ": "))
   (let ((input (read-char-exclusive)))
     (funcall
      (elt 
       (assoc
        (char-to-string input) words-funcs)
       2))))
words

This works nicely. Now, let us add a new function that looks up the word or selection on twitter. We just define a new function, and add the menu selection to the words-func variable.

(defun words-twitter ()
  (interactive)
  (browse-url
   (format
    "https://twitter.com/search?q=%s"
    (if (region-active-p)
        (url-hexify-string (buffer-substring (region-beginning)
                                             (region-end)))
      (thing-at-point 'word)))))

(add-to-list 'words-funcs
  '("w" "twitter" words-twitter)
  t) ; append
d ictionary words-dictionary
t hesaurus words-thesaurus
g oogle words-google
w twitter words-twitter

Finally, the most complicated idea: spelling and grammar. I know there is flyspell, and such, but they are build on an ancient dictionary. Here, for fun, we explore a web api. This next function is not a trivial one, and I will not explain it here beyond saying it sends a selection of text to a url, gets an xml response back, and that response is parsed and printed to a buffer by this function. The main point is to illustrate we can do interesting things with a selection of text!

(defun words-atd ()
  "Send paragraph at point to After the deadline for spell and grammar checking."
  (interactive)
  
  (let* ((url-request-method "POST")
         (url-request-data (format
                            "key=some-random-text-&data=%s"
                            (url-hexify-string
                             (thing-at-point 'paragraph))))
         (xml  (with-current-buffer
                   (url-retrieve-synchronously
                    "http://service.afterthedeadline.com/checkDocument")
                 (xml-parse-region url-http-end-of-headers (point-max))))
         (results (car xml))
         (errors (xml-get-children results 'error)))
    
    (switch-to-buffer-other-frame "*ATD*")
    (erase-buffer)
    (dolist (err errors)
      (let* ((children (xml-node-children err))
             ;; for some reason I could not get the string out, and had to do this.
             (s (car (last (nth 1 children))))
             ;; the last/car stuff doesn't seem right. there is probably
             ;; a more idiomatic way to get this
             (desc (last (car (xml-get-children children 'description))))
             (type (last (car (xml-get-children children 'type))))
             (suggestions (xml-get-children children 'suggestions))
             (options (xml-get-children (xml-node-name suggestions) 'option))
             (opt-string  (mapconcat
                           (lambda (el)
                             (when (listp el)
                               (car (last el))))
                           options
                           " ")))

        (insert (format "** %s ** %s
Description: %s
Suggestions: %s

" s type desc opt-string))))))

(add-to-list 'words-funcs
  '("s" "spell/grammar" words-atd)
  t) ; append
words-atd

My final words menu looks like:

If I have the cursor in the previous paragraph, run the words command and select "s" I get a buffer with these contents:

#+BEGINEXAMPLE

1 flyspell ** (spelling)

Description: (Spelling) Suggestions: flywheel flyball

2 are build on ** (grammar)

Description: (Auxiliary Verb Agreement) Suggestions: are built on

3 api ** (spelling)

Description: (Spelling) Suggestions: app ape apt ai ami

4 url ** (spelling)

Description: (Spelling) Suggestions: urn ure curl hurl burl

5 xml ** (spelling)

Description: (Spelling) Suggestions: xl ml

6 selection ** (suggestion)

Description: (Complex Expression) Suggestions: choice

7 an xml ** (grammar)

Description: (Wrong article) Suggestions: a xml

8 a selection of ** (grammar)

Description: (Hidden Verbs) Suggestions:

9 is parsed ** (grammar)

Description: (Passive voice) Suggestions:

10 selection ** (suggestion)

Description: (Complex Expression) Suggestions: choice

11 a selection of ** (grammar)

Description: (Hidden Verbs) Suggestions: #+ENDEXAMPLE

It might be nice to link back to those words, so you could click on them and fix them, but that is a beyond today's goal. In summary, today we looked at a framework to create a user-modifiable menu of commands that are launched from a single command. Here we called the command words, and then built up some different things we might want to do with the word or selection at point. While you can of course just remember the individual commands, remembering one command and then being prompted might have some advantages.

Copyright (C) 2014 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.7c

Discuss on Twitter