The Fennel programming language is one of my favorite programming languages. It gets a lot of things done right:

  • Modern syntax (Clojure-like, but better)
  • Immutable by default
  • Excellent pattern matching

If I ever find myself working with a Lua interpreter (Hammerspoon, Neovim, TIC-80, etc.), Fennel is often my go-to for concise and efficient code.

The question is, can I have a REPL for Hammerspoon? There are multiple options available, like nREPL via jeejah, but I find it limited compared to the REPL that comes with fennel-mode. Hammerspoon does come with an interactive shell, and I would like to adapt it to work with fennel-mode. In fact, it’s very possible. Hammerspoon comes with a hook, _consoleInputPreparser, that can be hacked to evaluate an expression. Essentially, we can run a REPL in a coroutine and resume it whenever the user inputs something.

(require :hs.ipc)

(local fennel (require :fennel))

(let [coro (coroutine.create fennel.repl)]
  (coroutine.resume coro {:readChunk (fn []
                                       (let [input (coroutine.yield)]
                                         (.. input "\n")))
                          :onValues (fn [xs]
                                      (print (table.concat xs "\t")))
                          :onError (fn [_ msg]
                                     (print msg))})
  (set hs._consoleInputPreparser (fn [s]
                                   (coroutine.resume coro s)
                                   "")))

Here is the result:

And of course, you can evaluate code and get completion with fennel-mode, too.