← Back to garden 🚀

output

How to switch between different IDEs with a single shortcut in MacOS and Raycast

Intro

Similar to Josean Martinez’s Window Management Workflow, I used to use Aerospace and map a single key to a specific space where I would have a specific app or type of apps. Here’s where the issue arose: I use many different IDEs and code editors like Xcode, IntelliJ, VSCode, and Android Studio, and I didn’t want to create a single keybinding for each one of them. So, I just wanted a keybinding that would open whatever IDE I have open, and if I’m using two, cycle between them.
So, for this, I’ve decided to create an Apple Script that will check if any of the specified apps is open/focused and either bring it to focus, open a default IDE, or cycle between multiple apps. I will also use Raycast’s ability to run Apple Scripts and assign keybindings to it.

Getting the bundle ID of every IDE/App

We first to know the Bundle ID of every app in our system so that is easily identifiable by the script. The following command will give you the ID an app on MacOS.

osascript -e 'id of app "Visual Studio Code"'

And here’s a list of common IDEs

  • Most common ones:
    1. IntelliJ Idea: com.jetbrains.intellij
    2. Android Studio: com.google.android.studio
    3. VsCode: com.microsoft.VSCode
    4. Zed: dev.zed.Zed
    5. Xcode: com.apple.dt.Xcode

Integration with Raycast

Now, we need to tell Raycast to create our Script, by searching “Create Script Command” and selecting Apple Script in the “Template” section.
Once we have created or template, you can find it in Raycast’s “Extensions” and assign a Hotkey to it.

The actual apple script

Once we have the list of apps we want to “group” with out keybinding, place them in the targetIDEBundleIDs collection, and also set defaultApp to the app you want to open in case none are opened.

#!/usr/bin/osascript

# Required parameters:
# @raycast.schemaVersion 1
# @raycast.title Open IDE
# @raycast.mode silent

# Documentation:
# @raycast.description Open the current IDE or falls back to a text editor
# @raycast.author fernando

property targetIDEBundleIDs : {"com.jetbrains.intellij", "com.google.android.studio", "dev.zed.Zed", "com.microsoft.VSCode", "com.apple.dt.Xcode"}
set defaultApp to "com.microsoft.VSCode"


set activeAppBundleID to ""
try
	tell application "System Events"
		set frontProcess to first process whose frontmost is true
		set activeAppBundleID to bundle identifier of frontProcess
	end tell
on error errMsg
	log "Error getting active application bundle identifier: " & errMsg
	display dialog "Could not determine the active application. Please ensure Script Editor has permission to control System Events." buttons {"OK"} default button 1 with icon caution
	return
end try


set runningAppBundleIDs to {}
try
	tell application "System Events"
		repeat with p in (application processes whose background only is false)
			try
				set bid to bundle identifier of p
				if bid is not missing value then
					set end of runningAppBundleIDs to bid
				end if
			end try
		end repeat
	end tell
on error errMsg
	log "Error getting list of running applications: " & errMsg
end try


set runningTargetIDEBundleIDs to {}
repeat with bid in runningAppBundleIDs
	if bid is in targetIDEBundleIDs then
		set end of runningTargetIDEBundleIDs to bid
	end if
end repeat

if (count of runningTargetIDEBundleIDs) is 0 then
	log "No target IDEs running. Opening Default."
	try
		tell application id defaultApp
			activate -- Launch and bring to front
		end tell
	on error
		log "Zed application not found or failed to open."
		display dialog "Zed application not found. Please ensure Zed is installed in your Applications folder."
	end try
	
else
	log "At least one target IDE is running."
	
	set otherRunningTargetIDEBundleIDs to {}
	-- log "running " &activeAppBundleID 
	-- log "apps running " &runningTargetIDEBundleIDs 
	set isRunning to false
repeat with anItem in runningTargetIDEBundleIDs
    -- Compare the string value of the active app with the contents of the reference
    if (activeAppBundleID as string) is equal to (contents of anItem as string) then
        set isRunning to true
        exit repeat -- Stop searching once a match is found
    end if
end repeat
	if isRunning then
		log "Active app (" & activeAppBundleID & ") is a running target IDE."
		
		repeat with bid in runningTargetIDEBundleIDs
			if (bid as string) is not equal to (activeAppBundleID as string) then
				set end of otherRunningTargetIDEBundleIDs to bid
			end if
		end repeat
		
		if (count of otherRunningTargetIDEBundleIDs) is not 0 then
			set appToActivateID to item 1 of otherRunningTargetIDEBundleIDs
			log "Other running target IDEs found. Activating: " &appToActivateID
			try
				tell application id (appToActivateID as text)
					activate
				end tell
			on error
				log "Failed to activate application with bundle ID: " & appToActivateID
			end try
		else
			log "Active target IDE is the only one running from the list. Doing nothing (already focused)."
		end if
	else
		set appToActivateID to item 1 of runningTargetIDEBundleIDs
		log "Active app " &count of runningTargetIDEBundleIDs  & " Other running IDEs:"  
		log "Switching to the first running target IDE found: "
		try
			tell application id appToActivateID
				activate
			end tell
		on error
			log "Failed to activate application with bundle ID: "
			display dialog "Failed to activate "
		end try
	end if
end if

log "Script finished."

Demo!

In the following demo, I’ve set ⌃⇧⌘ + D as my shortcut for my “Development” apps.
window-develop-script-demo-ezgif.com-video-to-gif-converter


References