I'd like to write a script that creates a new frame with emacsclient
if the user is already running an emacs server, but just starts up a
regular Emacs instance if he isn't.
--
-PJ
See e.g. (info "(emacs) emacsclient Options").
The other optional arguments recognized by `emacsclient' are listed
below:
`-a COMMAND'
`--alternate-editor=COMMAND'
Specify a command to run if `emacsclient' fails to contact Emacs.
This is useful when running `emacsclient' in a script. For
example, the following setting for the `EDITOR' environment
variable will always give you an editor, even if no Emacs server is
running:
EDITOR="emacsclient --alternate-editor emacs +%d %s"
As a special exception, if COMMAND is the empty string, then
`emacsclient' starts Emacs in daemon mode and then tries
connecting again.
The environment variable `ALTERNATE_EDITOR' has the same effect as
the `-a' option. If both are present, the latter takes precedence.
--
Deniz Dogan
emacsclient option:
-a, --alternate-editor=EDITOR
Editor to fallback to if server is not running
--
Oracle is the new evil
Ok, I guess I wasn't clear. I actually want to do MORE than just make
a new frame. :-) Say I run this command:
emacsclient -a emacs -e "(progn (select-frame (make-frame-on-display
\"$DISPLAY\")) (insert \"Hello, world!\"))"
If emacs is running in server mode it does what I wanted (make a
frame, select it, run some elisp), but if emacs isn't running in
server mode, I find myself editing a new file named '(progn
(select-frame (make-frame-on-display ":0.0")) (insert "Hello,
world!"))'
Any thoughts on a better way I could go about this?
Thanks,
PJ
Unfortunately, AFAIK, this piece of documentation is wrong. One is
forced to use much more baroque ways to do "the right thing". The bug
I suspect is emacsclient using some unportable switch (like
--daemon) for emacs, instead of using
-f server-start
on platforms which do not support --daemon. I needed to create a
batch file which does
emacs -f server-start %*
and give its name as the argument to --alternate-editor...
Ilya
I have in general three emacs instances running, so I use different
socket file names. I use the following script to detect the emacs
instances running with an active server.
Once you get the list of emacsen with an actitve server, you may compare
it with the list of running emacs processes.
------------------------------------------------------------------------
#!/usr/bin/clisp -ansi -q -Kfull -E iso-8859-1
;;;; -*- mode:lisp;coding:utf-8 -*-
;;;;**************************************************************************
;;;;FILE: mfod.lisp
;;;;LANGUAGE: Common-Lisp
;;;;SYSTEM: Common-Lisp
;;;;USER-INTERFACE: NONE
;;;;DESCRIPTION
;;;;
;;;; Shows all the emacs servers available, and let the user select one
;;;; on which to open a new frame.
;;;;
;;;;AUTHORS
;;;; <PJB> Pascal J. Bourguignon <p...@informatimago.com>
;;;;MODIFICATIONS
;;;; 2010-08-30 <PJB> Translated from bash...
;;;; 2009-09-12 <PJB> Created as a bash script.
;;;;BUGS
;;;;LEGAL
;;;; GPL
;;;;
;;;; Copyright Pascal J. Bourguignon 2010 - 2010
;;;;
;;;; This program is free software; you can redistribute it and/or
;;;; modify it under the terms of the GNU General Public License
;;;; as published by the Free Software Foundation; either version
;;;; 2 of the License, or (at your option) any later version.
;;;;
;;;; This program is distributed in the hope that it will be
;;;; useful, but WITHOUT ANY WARRANTY; without even the implied
;;;; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
;;;; PURPOSE. See the GNU General Public License for more details.
;;;;
;;;; You should have received a copy of the GNU General Public
;;;; License along with this program; if not, write to the Free
;;;; Software Foundation, Inc., 59 Temple Place, Suite 330,
;;;; Boston, MA 02111-1307 USA
;;;;**************************************************************************
(in-package "COMMON-LISP-USER")
;; Clean the packages imported into COMMON-LISP-USER:
(MAPC (LAMBDA (package) (UNUSE-PACKAGE package "COMMON-LISP-USER"))
(set-difference
(COPY-SEQ (PACKAGE-USE-LIST "COMMON-LISP-USER"))
(delete nil (list ;; A list of all the "CL" packages possible:
(FIND-PACKAGE "COMMON-LISP")
(FIND-PACKAGE "IMAGE-BASED-COMMON-LISP")))))
(load (make-pathname :name "SCRIPT" :type nil :version NIL :case :common
:defaults *load-pathname*))
(use-package "SCRIPT")
(setf *program-name* (pname))
;; (redirecting-stdout-to-stderr (load #p"/etc/gentoo-init.lisp"))
;; (redirecting-stdout-to-stderr
;; (let ((*load-verbose* nil)
;; (*compile-verbose* nil))
;; (load (make-pathname :name ".clisprc" :type "lisp" :case :local
;; :defaults (user-homedir-pathname)))
;; ;; (setf *features* (delete :testing-script *features*))
;; ))
;; (redirecting-stdout-to-stderr (asdf:oos 'asdf:load-op :split-sequence)
;; (asdf:oos 'asdf:load-op :cl-ppcre))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun function-named (sname pname)
(let ((pack (find-package pname)))
(and pack
(let ((sym (find-symbol sname pack)))
(and sym
(fboundp sym)
sym)))))
(defun getuid ()
(funcall (or (function-named "UID" "POSIX")
(function-named "getuid" "LINUX")
(function-named "GETUID" "LINUX")
(error "Cannot get the UID."))))
(defparameter *sockets*
(let ((uid (getuid)))
(sort
(mapcar (function namestring)
(remove-if-not (function probe-file)
(remove-duplicates
;; getting server is not so useful since directory will
;; return the truename...
(append (directory (format nil "/tmp/emacs~A/server" uid))
(directory (format nil "/tmp/emacs~A/server-*" uid)))
:test (function equalp))))
(function string-lessp))))
(defparameter *emacsen*
(let ((emacsen '()))
(dolist (socket *sockets* (reverse emacsen))
(let ((frames
(with-open-stream (frames (ext:run-program
"emacsclient"
:arguments (list (format nil "--socket-name=~A" socket)
"--eval"
"(mapcar (lambda (f) (list (frame-name f) (frame-display f))) (frame-list))")
:output :stream))
(read frames nil nil))))
(if frames
(push (list socket frames) emacsen)
(multiple-value-bind (all pid) (regexp:match "^.*server-\\([0-9]\\+\\)$" socket)
(if all
(let ((pid (regexp:match-string socket pid)))
(with-open-stream (ps (ext:run-program "ps" :arguments (list "-p" pid) :output :stream))
(unless (loop
:named search-emacs
:for line = (read-line ps nil nil)
:while line
:if (regexp:match "emacs" line)
:do (return-from search-emacs t)
:finally (return-from search-emacs nil))
(delete-file socket)
(setf *sockets* (delete socket *sockets*))))))))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-option ("list" "-l" "--list") ()
"List the available emacs servers."
(loop
:for i :from 1
:for (server frames) :in *emacsen*
:do (format t "~2D) ~30A ~:{~1@*~16A ~0@*~S~:^~%~35T~}~%"
i server frames)))
(define-option ("select" "-s" "--select") (index)
"Select the server at the given index (from 1 up) as the default server."
(let* ((index (parse-integer index))
(uid (getuid))
(server (ignore-errors (nth (1- index) *emacsen*))))
(if server
(ext:run-program "ln" :arguments (list "-sf"
(first server)
(format nil "/tmp/emacs~A/server" uid)))
(error "~A is not a server index. Please give an index between 1 and ~A"
index (length *emacsen*)))))
(defun xor (a b) (or (and a (not b)) (and (not a) b)))
(defun make-frame (socket-name &key on-display on-terminal)
(assert (xor on-display on-terminal))
(ext:run-program "emacsclient"
:arguments (cond
(on-display
(list
(format nil "--socket-name=~A" socket-name)
"--no-wait"
;; "--eval" (format nil "(make-frame-on-display \"~A\")"
;; )
"--create-frame"
"--display" on-display))
(on-terminal
(list
(format nil "--socket-name=~A" socket-name)
"--tty")))))
(define-option ("open" "-o" "--open") (index)
"Make a new frame from the server at the given index (from 1 up) on the current DISPLAY."
(let* ((index (parse-integer index))
(uid (getuid))
(server (ignore-errors (nth (1- index) *emacsen*)))
(display (ext:getenv "DISPLAY")))
(cond
((null server)
(error "~A is not a server index. Please give an index between 1 and ~A"
index (length *emacsen*)))
((null display)
(error "There is no DISPLAY environment variable."))
(t
(make-frame (first server) :on-display display)))))
(define-option ("terminal" "-t" "--open-on-terminal") (index)
"Make a new frame from the server at the given index (from 1 up) in the terminal."
(let* ((index (parse-integer index))
(uid (getuid))
(server (ignore-errors (nth (1- index) *emacsen*))))
(cond
((null server)
(error "~A is not a server index. Please give an index between 1 and ~A"
index (length *emacsen*)))
(t
(make-frame (first server) :on-terminal t)))))
(ext:exit
(if (null *emacsen*)
(progn
(format t "There is no emacs server~%")
EX-UNAVAILABLE)
(parse-options ext:*args*
(lambda ()
(call-option-function "help" '())
EX-NOINPUT))))
;;;; THE END ;;;;
------------------------------------------------------------------------
--
__Pascal Bourguignon__ http://www.informatimago.com/
A bad day in () is better than a good day in {}.
> Ok, I guess I wasn't clear. I actually want to do MORE than just make
> a new frame. :-) Say I run this command:
>
> emacsclient -a emacs -e "(progn (select-frame (make-frame-on-display
> \"$DISPLAY\")) (insert \"Hello, world!\"))"
>
> If emacs is running in server mode it does what I wanted (make a
> frame, select it, run some elisp), but if emacs isn't running in
> server mode, I find myself editing a new file named '(progn
> (select-frame (make-frame-on-display ":0.0")) (insert "Hello,
> world!"))'
>
> Any thoughts on a better way I could go about this?
>
> Thanks,
> PJ
Ok, I had an idea that seems to work:
======================================================================
#!/bin/sh
lisp="(insert \"Hello, world!\")"
emacsclient -a false --eval "(progn (select-frame
(make-frame-on-display \"$DISPLAY\")) $lisp)" 2>/dev/null
if [ $? != 0 ]; then
emacs --eval "$lisp"
fi
======================================================================
It feels like a kludge to use -a like that, but it does what I wanted
in the case that I was wondering about. I'm sure I'll think of more
corner cases (e.g., if ALTERNATE_EDITOR is set to "" in the
environment I should let emacsclient start the server instead of
forcing it to false), but for now I know what I'm doing. :-)
-PJ
> I have in general three emacs instances running, so I use different
> socket file names. I use the following script to detect the emacs
> instances running with an active server.
>
> Once you get the list of emacsen with an actitve server, you may compare
> it with the list of running emacs processes.
I haven't had time to look through the code yet, but thanks for
posting that script. Multiple server processes isn't a case I'm
directly familiar with, so it's good to have an example of how to
handle it.
Thanks!
-PJ