Using Hammerspoon to switch apps

A while ago I posted about Hammerspoon. I never got much time to play with it until this past weekend though.

One of the things I wanted to do with it is use is as an app launcher. I keep switching between apps (nothing new there!) but rather than Cmd+Tab my way through a list of apps I wanted an easy way of going to an app I want. Sort of having global hotkeys for them, if you know what I mean. Bear, for instance, let’s me assign “Ctrl+Cmd+B” to it so I can press these keys from anywhere and Bear opens up. If I could get this functionality in place for all apps, I could say assign “Ctrl+Cmd+C” for Visual Studio Code, “Ctrl+Cmd+O” for Outlook, and easily switch to that app without “Cmd+Tab”-ing through everything else.

This would also be useful for apps like Bitwarden which very irritatingly don’t have the option for a global hotkey. Currently, any time I want to lookup a password or use the generator I have to click in the menu bar to launch it. But if I could just assign a hotkey to launch it, that would be great.

One app I know that can do something like this is rcmd. It’s a good app, and the developer is very responsive and helpful too. In my initial enthusiasm for it, I bought it from the App Store after giving it a quick try – a decision I regret in retrospect, as it’s not very flexible. Yes, I can assign the right cmd key (rcmd) and a key to launch apps – so, for instance, “RCmd+C” to launch Visual Studio Code – but it’s not very flexible. I can’t, for example, use “C” as in my example above as rcmd defaults to “V”. There is a way to make it use “C” but it’s a bit of a round-about way, and I felt like the app fights against how I wanted to do things.

Anyway, this post is not about rcmd but Hammerspoon. How can I use Hammerspoon to quickly launch apps? ☺️

Quite easy actually, as it turns out. Thanks to this SO post I found that the following bit of code does it neatly:

Hah! Nice. The above maps “Ctrl+Cmd+C” to Calendar, and “Ctrl+Cmd+M” to Mail. I extended it a bit to also use “Cmd+Shift” for when a letter is already taken – e.g. “Cmd+Shift+C” to launch Visual Studio Code. It’s easy to do that, just duplicate the code:

Lovely!

I had done this much soon after I discovered Hammerspoon and that’s what I was doing until the weekend.

Mod 1

But obviously I wanted more. I don’t want to just launch an app, I also want to be able to switch between its Windows. For instance, I usually have 2 windows of Visual Studio Code and 2 windows of Firefox. If I do “Ctrl+Cmd+C” and reach Code, it will show one of the windows. Pressing it again won’t go to the next window, so I have to resort to the mouse of “Cmd+Tab” (or “Cmd+§” which I have mapped in Contexts to switch between windows of apps) – not ideal.

How can I make Hammerspoon switch between open windows of an app if I keep pressing the same keys? Again, SO to the rescue. Found this code there:

On the weekend I spent some time figuring out how this code works and slightly modified it. The result is:

I had to modify it from what I found on SO because the loop I had got going (what I found in the first SO post basically) had the app names as on disk (e.g. “Visual Studio Code”) – because that’s what Hammerspoon’s hs.application.launchOrFocus() uses but that’s not the name returned by hs.window.focusedWindow() (e.g. “Code”) when you try to get the app name of the focussed window. So I find the path of the application returned, then extract the name on disk from that, and use that to check if the focussed window is of an app I am interested in.

I also made some changes to cater to Finder as it has a different path. There could be other system apps too like this I suppose, got to cater to them later.

Honestly, I am quite pleased with this. I have no idea of Lua or Hammerspoon, but I spent a few hours figuring out what’s happening and Googled my way through making the modifications.

Mod 2

Fast forward to today. I am on holiday and there’s this enhancement to the above that I have been thinking of the past few days so I figured I should try and tackle it.

It’s good I can switch between multiple windows, but can I also modify this to switch between multiple apps? Like say, I assign both Visual Studio Code and Calendar to the same key and when I press it I want Hammerspoon to switch between just these two.

I am not sure how to tackle this though. Especially with the windows and apps. Say in the above example I have two windows of Visual Studio Code open. When I press “Ctrl+Cmd+C” it will take me to the first window. Press again, and it will take me to the second window. Press again… and what should it do? Take me to Calendar or back to the first window? If there was some way to capture the state that I was previously on the first window, now the second window, and I am still pressing the keys so I probably want to go to Calendar now then I could implement something to do that. But we are not keeping state anywhere, and it’s going to be too complicated doing that too I think. Instead I figured I should keep things simple and take the policy that if I switch to an app and it has more than one window then I keep to that app irrespective of whether any other app is assigned the key. But if that app has only one window then I can switch between apps. Makes sense?

So, to begin with this is how I’ll define the mappings:

Multiple apps are separated by semi-colons.

What next? Here’s what I was talking about above, in a better format:

  • If I press a key combo, switch to that app – a single app, or the first one in the list if there’s more than one.
    • So I need a way of splitting and getting the first item in a list of apps.
  • If I am already in that app, check if it has more than one window open
    • If yes, switch to the other window.
    • If no, do I have more than one app in my list?
      • If yes, then switch to the next app.
        • So I need a way of finding this app position in my list and then go to the next one. But if it’s the last one, then launch the first app.
      • If no, then do nothing. Or just launch the app anyway.

Inspired by this SO post which shows how to split a string I made this function to begin with:

It takes a list of apps – “Visual Studio Code; Code”, and the currently focussed app (“Visual Studio Code”, for example), and a separator character (defaults to the semi-colon) and it returns the next app I should launch. In this case it will be “Calendar” but if the currently focussed app was “Calendar” it will return “Visual Studio Code”.

It’s pretty simple, but as a Lua newbie I had to Google and read some docs (example & example) to make sense of things. It helped that I am already aware of things like regular expressions and have a programming/ scripting background.

Next I made the following function:

This returns the first app in a list of apps; or the single entry if it’s just one app.

With this I was able to modify the previous function:

I just had to make some minor changes to my existing code to use the new functions I created – which I highlighted above. Here’s what the new functions do in the context of what I wrote above:

  • If I press a key combo, switch to that app – a single app, or the first one in the list if there’s more than one.
    • So I need a way of splitting and getting the first item in a list of apps. This is what
      getFirstAppFromList does.
  • If I am already in that app, check if it has more than one window open
    • If yes, switch to the other window.
    • If no, do I have more than one app in my list?
      • If yes, then switch to the next app.
        • So I need a way of finding this app position in my list and then go to the next one. But if it’s the last one, then launch the first app.
        • I created getAppToLaunchFromList which will do this. It will return the app if it’s the only one in the list, or the next one.
      • If no, then do nothing. Or just launch the app anyway.

That’s all for now!