-
Notifications
You must be signed in to change notification settings - Fork 47
Working with Hammerspoon
There's an undocumented function to preproccess console input: hs._consoleInputPreparser
You can set this to always wrap your input in hs.inspect
with a custom depth:
hs._consoleInputPreparser = function(s) return 'hs.inspect('..s..', { depth = 2 } )' end
hs._consoleInputPreparser = function(s) return 'u.toJson('..s..')' end
If this is your first time experimenting with Hammerspoon, I can recommend zzamboni's fantastic ebook, Getting Started with Hammerspoon.
When you first startup Hammerspoon, you'll see a macOS notification informing you that Hammerspoon couldn’t find a configuration file. To fix this, run the commands below in a terminal window:
❯ touch $HOME/.hammerspoon/init.lua
❯ vim $HOME/.hammerspoon/init.lua
Let's add something to your Hammerspoon config just to confirm that everything is working properly:
hs.hotkey.bindSpec({ { "cmd", "alt", "ctrl" }, "s" },
function()
hs.notify.show("Hello from Hammerspoon!", "Everything looks good here, cap'n.", "Next, let's set up stackline.")
end
)
Add the snippet above to your Hammerspoon config file and save the file.
Then, click the 'Hammer' icon in your menu bar and select "Reload Config".
Now, when you enter cmd + alt + ctrl
, a macOS notification should appear with the message you entered above.
Congrats! Now you know enough about Hammerspoon to setup Stackline. If you'd like to continue learning more Hammerspoon's powers, see this blog post will be your guide.
lua
is a unique language. It can be beautiful once you understand it, and perplexing if you don't (as I didn't when first working on Stackline). If, like me, you're not yet comfortable with lua
check out the 'Working with lua' page for some quality-of-life tips before reading on.
The sections below represent areas of Hammerpsoon that required research on my part. It's not an exhuastive list of tips for beginners, but it might save you some time.
hs.task
is async. There are 2 ways to deal with this:
hs.timer.waitUntil(pollingCallback, func)
hs.task.new(…):start():waitUntilUNex()
The 1st polls a callback to check if the expected result of the async task has materialized.
The 2nd makes hs.task
behave synchronously.
The docs strongly discourage use of the 2nd approach, but as long as there isn't background work that could be done while waiting (there isn't in the use case I'm thinking of), then it should be slightly faster than polling since the callback will fire immediately when the task completes. It also saves the cycles needed to poll in the first place.
-- Wait until the win.stackIdx is set from async shell script
hs.task.new("/usr/local/bin/dash", function(_code, stdout, stderr)
callback(stdout)
end, {cmd, args}):start():waitUntilExit()
-- NOTE: timer.waitUntil is like 'await' in javascript
hs.timer.waitUntil(winIdxIsSet, function() return data end)
-- Checker func to confirm that win.stackIdx is set
-- For hs.timer.waitUntil
-- NOTE: Temporarily using hs.task:waitUntilExit() to accomplish the
-- same thing
function winIdxIsSet()
if win.stackIdx ~= nil then
return true
end
end
Yet another project has a similar take:
if canvas then
canvas:delete()
end
Clear any pre-existing status display canvases
for state, display in pairs(self.displays) do
if display ~= nil then
display:delete()
self.displays[state] = nil
end
end