Skip to content

Instantly share code, notes, and snippets.

@akagr
Last active June 14, 2024 03:46
Show Gist options
  • Save akagr/18ec650bc5f7e0ef663af9a30ce02973 to your computer and use it in GitHub Desktop.
Save akagr/18ec650bc5f7e0ef663af9a30ce02973 to your computer and use it in GitHub Desktop.
MacOS launch helper to start emacs daemon or attach to one
-- ███████╗███╗░░░███╗░█████╗░░█████╗░░██████╗
-- ██╔════╝████╗░████║██╔══██╗██╔══██╗██╔════╝
-- █████╗░░██╔████╔██║███████║██║░░╚═╝╚█████╗░
-- ██╔══╝░░██║╚██╔╝██║██╔══██║██║░░██╗░╚═══██╗
-- ███████╗██║░╚═╝░██║██║░░██║╚█████╔╝██████╔╝
-- ╚══════╝╚═╝░░░░░╚═╝╚═╝░░╚═╝░╚════╝░╚═════╝░
--
-- Open this script with 'Script Editor' on MacOS, then save it
-- inside /Applications as an 'Application', not 'Script'.
--
-- Prerequisites:
--
-- 1. There should be Emacs in your /Applications
-- If you downloaded emacs-plus from homebrew:
-- ln -s /usr/local/Cellar/emacs-plus@27/27.2/Emacs.app /Applications/Emacs.app
--
-- 2. Make sure you have a /usr/local/bin/emacsclient
-- If you don't, update the script below to the correct path
--
-- Akash Agrawal <akagr@outlook.com>
tell application "Finder"
try
log "checking for existing emacs client frames"
set frameVisible to do shell script "/usr/local/bin/emacsclient -n -e '(> (length (frame-list)) 1)'"
if frameVisible is not "t" then
log "server exists, attaching a new client frame"
do shell script "/usr/local/bin/emacsclient -c -n"
else
log frameVisible
log "server and window exists, bringing it to foreground"
end if
on error
log "starting server"
do shell script "/usr/local/bin/emacs --daemon"
do shell script "/usr/local/bin/emacsclient -c -n"
end try
tell application "Emacs" to activate
end tell
# Complimentary shell helper to add an 'em' command if you're opening files from terminal.
# It will try to connect to a running emacs daemon. If not found, it starts one.
#
# Source this file from your ~/.zlogin, ~/.zshrc, ~/.bashrc etc,
# or add this code directly to those files.
#
# Example Usage:
# > em ~/.zshrc
#
# Akash Agrawal <akagr@outlook.com>
em () {
# Check if a frame(window) already exists
emacsclient -n -e "(if (> (length (frame-list)) 1) 't)" | grep -q t
# If it doesn't create a new window (start server if not running)
# Else, use the same window to open the file.
if [ "$?" = "1" ]; then
emacsclient -c -n -a "" "$@"
else
emacsclient -n -a "" "$@"
fi
}
@rnkn
Copy link

rnkn commented May 24, 2021

Hey! Found this via Sacha Chua's blog.

This is actually a bit more complicated than it needs to be. Just put this in ~/Library/LaunchAgents:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
    <dict>
        <key>Label</key>
        <string>YOURDOMAIN.emacs</string>
        <key>Disabled</key>
        <false/>
        <key>EnvironmentVariables</key>
        <dict>
            <key>PATH</key>
            <string>/usr/local/bin:/usr/bin:/usr/sbin:/bin:/sbin</string>
        </dict>
        <key>ProgramArguments</key>
        <array>
            <string>/usr/local/bin/emacs</string>
            <string>-nw</string>
            <string>--fg-daemon</string>
        </array>
        <key>ProcessType</key>
        <string>Interactive</string>
        <key>KeepAlive</key>
        <true/>
        <key>RunAtLoad</key>
        <true/>
        <key>StandardOutPath</key>
        <string>/Users/USER/Library/Logs/emacs.log</string>
        <key>StandardErrorPath</key>
        <string>/Users/USER/Library/Logs/emacs-error.log</string>
    </dict>
</plist>

Then alias emacsclient -nw and emacsclient -c to something easier.

@akagr
Copy link
Author

akagr commented May 24, 2021

@rnkn Thanks for your suggestion. Tried it out but I feel it's solving a different problem.

What I'm trying to do is open emacs daemon from spotlight if it's not already running, or connect to it and open a frame. Creating a launch agent will start the daemon when the system starts, yes, but if for whatever reason I have to restart it, I'll have to do it from the CLI (right?).

That said, I think this can be a good addition to start a daemon so most timee, I don't have to do that initial slow load!

@rnkn
Copy link

rnkn commented May 24, 2021

Ah I see! Yes I was confused by "launch agent" (this is what launchd calls its background services). This would start at login and now I've edited the plist to include the KeepAlive boolean, which will automatically restart emacs when it's killed with either command-Q or M-x kill-emacs.

You can then use Automator.app to create an application with a Run Shell Script action that runs /usr/local/bin/emacsclient -c and save that as Emacs.app, which will be available via Spotlight and Launchpad.

@AtomicNess123
Copy link

Does M-x server-start not accomplish the same thing as this script? I may be confused. Newbie here.

@akagr
Copy link
Author

akagr commented May 25, 2021

@AtomicNess123 Kinda. To run M-x start-server, you have to have already started emacs. This script allows you to open spotlight, enter something like 'emacs' or 'em' or whatever you name it, and open emacs in daemon mode with a visible frame.

If you close emacs (without killing server), opening emacs from spotlight again will just connect to the running instance, so it's gonna be fast.

@AtomicNess123
Copy link

Thanks, I think I get the idea :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment