exupero's blog

Hammerspoon with Fennel

For the last few years I used Phoenix for window management on my Mac. Phoenix is configured with JavaScript, so I compiled ClojureScript to a single JavaScript configuration file, and while my setup wasn't elaborate (mostly keyboard shortcuts for focusing particular applications and rearranging windows), I was often happy to have a full-fledged programming language rather than a simplified configuration language like the one used by my previous window manager, Slate. When I discovered Fennel not long ago, I went looking for interesting projects that used Lua and found Hammerspoon.

I wasn't that desperate to switch from Phoenix to a new window manager, but my interest was piqued by Hammerspoon's ability to draw on the desktop or in floating widgets. Replacing Phoenix turned out to be easy, however, as Hammerspoon comes with most of the features I'd built myself in Phoenix. The one hiccup was figuring out how to configure Hammerspoon with Fennel instead of Lua.

At first I tried just editing init.fnl and running a watcher to automatically compile to Lua on changes, but Hammerspoon's suggested auto-reloading code wouldn't pick up the changes, and I suspect Bash's redirection to write the file truncated it and caused Hammerspoon to lose the file handle.

The solution, mined from someone's GitHub code, is to embed Fennel:

package.path =
  package.path .. ";" .. os.getenv("HOME") .. "/.hammerspoon/?.lua"

fennel = require('fennel')
fennel.path =
  package.path .. ";" .. os.getenv("HOME") .. "/.hammerspoon/?.fnl"
table.insert(package.loaders or package.searchers, fennel.searcher)

require 'main'

In the code above, main refers to main.fnl, which is compiled to Lua on import.

With this init.lua, I haven't had any trouble automatically reloading main.fnl when it changes.