I'm new to emacs. I'm a spacemacs implant, really love ORG mode and evil mode. Been using for the past year or so.
I've read some of the emacs documentation, which is fantastically written. But I'm struggling with getting emacsclient
and emacs daemon to work with a single frame.
I'm ok with scripting bash outside of emacs, but if there is an 'emacs configuration' way, I would prefer that - assume I'm clueless here.
What I want to achieve
With a script named 'editfile' on my path, running:
editfile file-path
NOTE: file-path is optional.
I would like the following to happen:
- If the emacs-daemon isn't running, start it up (with the correct chemacs2 profile)
- If no frames are open, open
file-path
in a new frame - If a frame exists, switch focus to that frame and open
file-path
in the already existing frame.
I got #1 solved (with chemacs2 no less), and I love it.
I need help with #2 and #3, Currently I always open a new frame, and I would rather it didn't.
My editfile
script looks like this:
#! /usr/bin/env bash
MY_EMACS_PROFILE=${CHEMACS_PROFILE:-spacemacs}
MY_SILENT="/dev/null"
MY_EMACS_CREATEFRAME="--create-frame"
MY_EMACS_NO_WAIT="--no-wait"
# MY_SILENT="/dev/tty"
# MY_EMACS_NO_WAIT=""
if [ "$DISPLAY" = "" ]; then
MY_EMACS_NO_WAIT=""
fi
function q_() { printf "'%s'\n" "${@}"; }
function myemacs_full() {
local _cli="$(q_ "${@}")"
_cli="${_cli// /\\ }"
_cli=$(printf "$_cli")
_cli=$(printf "/usr/bin/emacsclient \
--socket-name=$MY_EMACS_PROFILE \
$MY_EMACS_CREATEFRAME \
$MY_EMACS_NO_WAIT \
--alternate-editor='sh -c \"( \
/usr/bin/emacs --with-profile $MY_EMACS_PROFILE --daemon \
); echo ''Daemon started'' > $MY_SILENT; ( \
/usr/bin/emacsclient \
--socket-name=$MY_EMACS_PROFILE \
$MY_EMACS_NO_WAIT \
--create-frame \
${_cli//\'/\'\'} \
)\"' \
$_cli" \
)
2>$MY_SILENT eval $_cli
}
myemacs_full "${@}"
Here's what is going on there:
- I'm building a
_cli
command line which I theneval
- the
_cli
command comprises of callingemacsclient
with the following options:--socket-name...
- the name of the server's socket. (That's because I use chemacs2, and more than one daemon might be running, this is the way to distinguish between them.--create-frame
- I would want this to be dynamic, and I'm trying to figure out how to determine whether this is required or not.
Currently I always create a new frame.$MY_EMACS_NO_WAIT
will either be empty or--no-wait
(text/terminal mode should use wait, GUI mode should not)--alternate-editor...
- If the daemon isn't listening, spin up the daemon and run theemacsclient
one more time.
The daemon is launched with chemacs2's--with-profile
argument.
This is the main reason I'm using the funky process of building$_cli
withprintf
andeval
Update #1 : Things I've tried:
- Tyler's suggestion below
isn't working for me.
Where it fails for me is the need to pass--with-profile=spacemacs
(or any other profile name) to the daemon. With the default, it is not passed.
Update #2 : How --alternate-editor
works
When an empty string is provided and the daemon isn't live yet, observing this with htop filtered on the string emacs
, emacs --daemon
is run, and if --socker=<socketname>
is specificed, that parameter is passed as well.
Then the frame is opened, however, I did not see any new process.
To better understand what is passed to the alternate-editor, I wrote a very simple /tmp/tst
script, and made it executable:
/usr/bin/env bash
echo "tst called with: [${@}]"
When I run emacsclient --socket=spacemacs --no-wait --alternate-editor="/tmp/tst a b" -- --with-profile=spacemacs
, the output is:
emacsclient: can't find socket; have you started the server? emacsclient: To start the server in Emacs, type "M-x server-start". tst called with: [a b --with-profile=spacemacs]
The first 2 lines are expected, the last line shows what is passed to the alternate editor.
Possible solutions (?)
The way I see it, to resolve this, at least one of the following strategies might work:
- Have the script above determine whether any frames are already open,
and clear the
$MY_EMACS_CREATEFRAME
variable. - Have something in the
init.el
that will make sure to reuse the latest active frame.
However, I have failed to figure out how to achieve either.
Reaching out to the community for help
If you have a suggestion on how to do this, I'd love to learn.
If you think I am doing this completely wrong, I would love to learn of better/different ways to achieve this.