In this second article about Hammerspoon, we look into Spoons, modules written in Lua which can be easily installed and loaded into Hammerspoon to provide ready-to-use functionality. Spoons provide a predefined API to configure and use them. They are also a good way to share your own work with other users.
See also the first article in this series.
Using a Spoon to locate your mouse
As a first example, we will use the MouseCircle spoon, which allows us to set up a hotkey that displays a color circle around the current location of the mouse pointer for a few seconds, to help you locate it.
To install the spoon, download its zip file from https://github.com/Hammerspoon/Spoons/raw/master/Spoons/MouseCircle.spoon.zip, unpack it, and double-click on the resulting MouseCircle.spoon
file. Hammerspoon will install the Spoon under ~/.hammerspoon/Spoons/
.
Once a Spoon is installed, you need to use the hs.loadSpoon()
function to load it. Type the following in the Hammerspoon console, or add it to your init.lua
file and reload the configuration:
hs.loadSpoon("MouseCircle")
After a spoon is loaded, and depending on what it does, you may need to configure it, assign hotkeys, and start it. A spoon’s API is available through the spoon.<SpoonName>
namespace. To learn the API you need to look at the spoon documentation page. In the case of MouseCircle, a look at http://www.hammerspoon.org/Spoons/MouseCircle.html reveals that it has two methods (bindHotkeys()
and show()
) and one configuration variable (color
) available under spoon.MouseCircle
.
The first API call is spoon.MouseCircle:bindHotkeys()
, which allows us to set up a hotkey that shows the mouse locator circle around the location of the mouse pointer. Let’s say we wanted to bind the mouse circle to Ctrl-⌘-Alt-d. According to the MouseCircle documentation, the name for this action is show
, so we can do the following:
spoon.MouseCircle:bindHotkeys({
show = { { "ctrl", "cmd", "alt" }, "d" }
})
Once you do this, press the hotkey and you should see a red circle appear around the mouse cursor, and fade away after 3 seconds.
The second API call in the MouseCircle spoon is show()
, which triggers the functionality of showing the locator circle directly. Let’s try it – type the following in the console:
Most spoons are structured like this: you can set up hotkeys to trigger the main functionality, but you can also trigger it via method calls. Normally you won’t use these methods, but their availability makes it possible for you to use spoon functionality from our own configuration, or from other spoons, to create further automation.
spoon.MouseCircle.color
is a public configuration variable exposed by the spoon, which specifies the color that will be used to draw the circle. Colors are defined according to the documentation for the hs.drawing.color
module. Several color collections are supported, including the OS X system collections and a few defined by Hammerspoon itself. Color definitions are stored in Lua tables indexed by their name. For example, you can view the hs.drawing.color.hammerspoon
table, including the color definitions, by using the convenient hs.inspect
method on the console:
> hs.inspect(hs.drawing.color.hammerspoon)
{
black = {
alpha = 1,
blue = 0.0,
green = 0.0,
red = 0.0
},
green = {
alpha = 1,
blue = 0.0,
green = 1.0,
red = 0.0
},
osx_red = {
alpha = 1,
blue = 0.302,
green = 0.329,
red = 0.996
},
osx_green = {
...
If we wanted to make the circle green, we can assign the configuration value like this:
spoon.MouseCircle.color = hs.drawing.color.hammerspoon.green
The next time you invoke the show()
method, either directly or through the hotkey, you will see the circle in the new color.
By now you know enough to use spoons with Hammerspoon’s native capabilities: look for the ones you want, download and install them by hand, and configure them in your init.lua
using their configuration variables and API. In the next sections you will learn more about the minimum API of spoons, and how to install and configure spoons in a more automated way.
The Spoon API
The advantage of using spoons is that you can count on them to adhere to a defined API, which makes it easier to automate their use. Although each spoon is free to define additional variable and methods, the following are standard:
-
SPOON:init()
is called automatically (if it exists) by hs.loadSpoon
after loading the spoon, and can be used to initialize variables or anything else needed by the Spoon.
-
SPOON:start()
should exist if the spoon requires any ongoing or background processes such as timers or watchers of any kind.
-
SPOON:stop()
should exist if start()
does, to stop any background processes that were started by start()
.
-
SPOON:bindHotkeys(map)
is exposed by spoons which allow binding hotkeys to certain actions. Its map
argument is a Lua table with key/value entries of the following form: ACTION = { MODS, KEY }
, where ACTION is a string defined by the spoon (multiple such actions can be defined), MODS is a list of key modifiers (valid values are "cmd"
, "alt"
, "ctrl"
and "shift"
), and KEY is the key to be bound, as shown in our previous example. All available actions for a spoon should be listed in its documentation.
Automated Spoon installation and configuration
Once you develop a complex Hammerspoon configuration using spoons, you may start wondering if there is an easy way to manage them. There are no built-in mechanisms for automatically installing spoons, but you can use a spoon called SpoonInstall that implements this functionality. You can download it from http://www.hammerspoon.org/Spoons/SpoonInstall.html. Once installed, you can use it to declaratively install, configure and run spoons. For example, with SpoonInstall you can use the MouseCircle spoon as follows:
hs.loadSpoon("SpoonInstall")
spoon.SpoonInstall:andUse("MouseCircle", {
config = {
color = hs.drawing.color.osx_red,
},
hotkeys = {
show = { { "ctrl", "cmd", "alt"}, "d" }
}})
If the MouseCircle spoon is not yet installed, spoon.SpoonInstall:andUse()
will automatically download and install it, and set its configuration variables and hotkeys according to the declaration.
If there is nothing to configure in the spoon, spoon.SpoonInstall:andUse("SomeSpoon")
does exactly the same as hs.loadSpoon("SomeSpoon")
. But if you want to set configuration variables, hotkey bindings or other parameters, the following keys are recognized in the map provided as a second parameter:
-
config
is a Lua table containing keys corresponding to configuration variables in the spoon. In the example above, config = { color = hs.drawing.color.osx_red }
has the same effect as setting spoon.MouseCircle.color = hs.drawing.color.osx_red
-
hotkeys
is a Lua table with the same structure as the mapping parameter passed to the bindHotkeys
method of the spoon. In our example above, hotkeys = { show = { { "ctrl", "cmd", "alt"}, "d" } }
automatically triggers a call to spoon.MouseCircle:bindHotkeys({ show = { { "ctrl", "cmd", "alt"}, "d" } })
.
-
loglevel
sets the log level of the logger
attribute within the spoon, if it exists. The valid values for this attribute are ’nothing’, ’error’, ‘warning’, ‘info’, ‘debug’, or ‘verbose’.
-
start
is a boolean value which indicates whether to call the Spoon’s start()
method (if it has one) after configuring everything else.
-
fn
specifies a function which will be called with the freshly-loaded Spoon object as its first argument. This can be used to execute other startup or configuration actions that are not covered by the other attributes. For example, if you use the Seal spoon (a configurable launcher), you need to call its loadPlugins()
method to specify which Seal plugins to use. You can achieve this with something like this:
spoon.SpoonInstall:andUse("Seal",
{ hotkeys = { show = { {"cmd"}, "space" } },
fn = function(s)
s:loadPlugins({"apps", "calc", "safari_bookmarks"})
end,
start = true,
})
-
repo
indicates the repository from where the Spoon should be installed if needed. Defaults to "default"
, which indicates the official Spoon repository at http://www.hammerspoon.org/Spoons/. I keep a repository of unofficial Spoons at http://zzamboni.org/zzSpoons/, and others may be available by the time you read this.
-
disable
can be set to true
to disable the Spoon (easier than commenting it out when you want to temporarily disable a spoon) in your configuration.
Managing repositories and spoons using SpoonInstall
Apart from the andUse()
“all-in-one” method, SpoonInstall has methods for specific repository- and spoon-maintenance operations. As of this writing, there are two Spoon repositories: the official one at http://www.hammerspoon.org/Spoons/, and my own at http://zzamboni.org/zzSpoons/, where I host some unofficial and in-progress Spoons.
The configuration variable used to specify repositories is SpoonInstall.repos
. Its default value is the following, which configures the official repository identified as “default”:
{
default = {
url = "https://github.com/Hammerspoon/Spoons",
desc = "Main Hammerspoon Spoon repository",
}
}
To configure a new repository, you can define an extra entry in this variable. The following code creates an entry named “zzspoons” for my Spoon repository:
spoon.SpoonInstall.repos.zzspoons = {
url = "https://github.com/zzamboni/zzSpoons",
desc = "zzamboni's spoon repository",
}
After this, both “zzspoons” and “default” can be used as values to the repo
attribute in the andUse()
method, and in any of the other methods that take a repository identifier as a parameter. You can find the full API documentation at http://www.hammerspoon.org/Spoons/SpoonInstall.html.
Conclusion
Spoons are a great mechanism for structuring your Hammerspoon configuration. If you want an example of a working configuration based almost exclusively on Spoons, you can view my own Hammerspoon configuration at https://github.com/zzamboni/dot-hammerspoon.