index all rss twitter github linkedin email

Álvaro Ramírez

07 July 2018 Emacs utilities for your OS

Narrowing utilities are a wonderful way of increasing productivity. I have a few workflows using Emacs's Helm framework.

There are great productivity boosters like Alfred and Quicksilver for macOS, with batteries included.

If you're a tinkerer, you'd enjoy the powerful Hammerspoon. Like elisp gluing all things Emacs, Hammerspoon uses Lua to glue all things macOS. You can build your own narrowing utilities using chooser and a little Lua.

local chooser = hs.chooser.new(function(choice)
      hs.alert.show(choice['text'])
end)

chooser:choices({
      {
         ["text"] = "Alfred\n",
         ["subText"] = "macOS only\n",
      },
      {
         ["text"] = "Quicksilver\n",
         ["subText"] = "macOS only\n",
      },
      {
         ["text"] = "Hammerspoon\n",
         ["subText"] = "macOS only\n",
      },
      {
         ["text"] = "Emacs\n",
         ["subText"] = "is everywhere :)\n",
      },
})
chooser:show()

chooser.png

Howard Abrams's post on Capturing Content for Emacs inspired me to look at gluing Emacs and macOS to launch my own cross-platform narrowing utilities.

I've also taken this opportunity to look at Oleh Krehel's wonderful completion package: Ivy. We can use it to build a macOS narrowing utility.

Ivy is remarkably easy to use. Turns out, ivy-read is all you need. A simple Emacs completion can be accomplished with little elisp.

(ivy-read "Hello ivy: "
          '("One "
            "Two "
            "Three "
            "Four "))

simple_ivy.png

Pretty nifty. Let's make this completion more accessible from the rest of the OS. To do so, we create a separate Emacs frame and make it pretty. We also want it to interact with the OS. We'll use ivy-read's :action to invoke a tiny bit of AppleScript.

Oh and we'll also use some funny quotes to tease ourselves about our beloved editor.

(with-current-buffer (get-buffer-create "*modal-ivy*")
  (let ((frame (make-frame '((auto-raise . t)
                             (background-color . "DeepSkyBlue3")
                             (cursor-color . "MediumPurple1")
                             (font . "Menlo 15")
                             (foreground-color . "#eeeeec")
                             (height . 20)
                             (internal-border-width . 20)
                             (left . 0.33)
                             (left-fringe . 0)
                             (line-spacing . 3)
                             (menu-bar-lines . 0)
                             (minibuffer . only)
                             (right-fringe . 0)
                             (tool-bar-lines . 0)
                             (top . 48)
                             (undecorated . t)
                             (unsplittable . t)
                             (vertical-scroll-bars . nil)
                             (width . 110)))))
    (set-face-attribute 'ivy-minibuffer-match-face-1 frame
                        :background nil
                        :foreground nil)
    (set-face-attribute 'ivy-minibuffer-match-face-2 frame
                        :background nil
                        :foreground "orange1")
    (set-face-attribute 'ivy-minibuffer-match-face-3 frame
                        :background nil
                        :foreground "orange1")
    (set-face-attribute 'ivy-minibuffer-match-face-4 frame
                        :background nil
                        :foreground "orange1")
    (set-face-attribute 'ivy-current-match frame
                        :background "#ffc911"
                        :foreground "red")
    (set-face-attribute 'minibuffer-prompt frame
                        :foreground "grey")
    (let ((ivy-height 20)
          (ivy-count-format ""))
      (ivy-read "Emacs acronyms: "
                '(" Emacs: Escape-Meta-Alt-Control-Shift "
                  " Emacs: Eight Megabytes And Constantly Swapping "
                  " Emacs: Even a Master of Arts Comes Simpler "
                  " Emacs: Each Manual's Audience is Completely Stupified "
                  " Emacs: Eventually Munches All Computer Storage "
                  " Emacs: Eradication of Memory Accomplished with Complete Simplicity "
                  " Emacs: Easily Maintained with the Assistance of Chemical Solutions "
                  " Emacs: Extended Macros Are Considered Superfluous "
                  " Emacs: Every Mode Accelerates Creation of Software "
                  " Emacs: Elsewhere Maybe All Commands are Simple "
                  " Emacs: Emacs Makes All Computing Simple "
                  " Emacs: Emacs Masquerades As Comfortable Shell "
                  " Emacs: Emacs My Alternative Computer Story "
                  " Emacs: Emacs Made Almost Completely Screwed "
                  " Emacs: Each Mail A Continued Surprise "
                  " Emacs: Eating Memory And Cycle-Sucking "
                  " Emacs: Elvis Masterminds All Computer Software "
                  " Emacs: Emacs Makes A Computer Slow" )
                :action (lambda (funny-quote)
                          (async-shell-command (format "osascript -e 'tell app \"System Events\" to display dialog \"%s\" buttons {\"OK\"}'" funny-quote)))
                :unwind (lambda ()
                          (shell-command "/Applications/Hammerspoon.app/Contents/Resources/extensions/hs/ipc/bin/hs -c 'backFromEmacs()'")
                          (delete-frame)
                          (other-window 1))))))

ivy_frame.gif

So where's all this going? I wrote a utility to extract all links from this page's org file and make them easily searchable from anywhere on macOS by invoking ⌥-W.

The keys are bound using Lua, Hammerspoon, and emacsclient. This works well on macOS, but there are alternatives for other operating systems.

hs.execute("emacsclient -ne \""..elisp.."\" -s /tmp/emacs*/server")

Here's the resulting utility in action:

ivy_links.gif

These integrations look promising. They enable me to bring cross-platform Emacs utilities into areas I hadn't considered.