Description
The default behavior when invoking emacsclient is a little conservative.
Check out this comment from
emacsclient.c:
/* Unless we are certain we don't want to occupy the tty, send our
tty information to Emacs. For example, in daemon mode Emacs may
need to occupy this tty if no other frame is available. */
From your description and comments, it sounds like you're trying to start the
Emacs server on demand while also using the -n
flag. The "for example" comment here is
why emacsclient -n -a '' FILE
doesn't satisfy what you're looking for when no server is running.
- The
-a ''
logic starts up a daemon.
- Then
emacsclient
tells it to create a new terminal frame, because that's
the default unless you're evaluating elisp.
- The
-n
logic immediately kills that new terminal frame.
If you could change Step 2 to create a new graphical frame by default, then
emacsclient -n -a '' FILE
would do what you want.
Elisp Solution
You can cause Emacs to create a new graphical frame by default if you advise
the function server-process-filter
like so:
(defadvice server-process-filter (before prefer-graphical activate)
;; STRING is a sequence of commands sent from emacsclient to the server.
(when (and
;; Check that we're editing a file, as opposed to evaluating elisp.
(string-match "-file" string)
;; Check that there are no frames beyond the Emacs daemon's terminal.
(daemonp)
(null (cdr (frame-list)))
(eq (selected-frame) terminal-frame)
;; Check that we have a graphical display.
;; `display-graphic-p' doesn't work here.
(getenv "DISPLAY"))
(setq string (concat
;; STRING must be all one line, but comes to us
;; newline-terminated. Strip off the trailing newline.
(replace-regexp-in-string "\n$" "" string)
;; Add the commands to create a graphical frame.
"-window-system "
"-display " (getenv "DISPLAY")
;; Add back the newline.
"\n"))))
Put that in your init-file, then, as said, emacsclient -n -a '' FILE
and Bob's your uncle.
Compare to Shell Solution
On the one hand, I can point to a few advantages to using this defadvice
approach as compared to using the
script suggested by Archenoth
#!/bin/bash
emacs --eval '(server-start)' $* &
as the alternate editor. With the defadvice:
save-buffers-kill-terminal
(C-x C-c
by default) behaves consistently
across all frames. It never kills the Emacs process, because every frame
is always a client frame.
- The daemon's terminal frame hangs around. Commands like
find-grep
that
shell out to external processes behave better when the dumb terminal is
there. At least, I experience fewer shell-escaping related headaches.
On the other hand ... yeah.
- That shell script is beautifully simple.
- Advising Emacs' communication protocol is not.
Conclusion
Maybe there's a compromise? This is the best I could come up with. You set it as your $EDITOR.
#!/bin/sh
emacsclient -e "(frames-on-display-list \"${DISPLAY}\")" 1>/dev/null 2>/dev/null
if [ "$?" = "1" ]; then
emacsclient -c -n -a "" "$@"
else
emacsclient -n "$@"
fi