Invoking emacs daemon and client easily

UPDATE 2015-07-16 β€” this is much better: http://superuser.com/questions/358037/emacsclient-create-a-frame-if-a-frame-does-not-exist/862809#862809




#!/bin/bash

# https://hugoheden.wordpress.com/2009/03/21/invoking-emacs-daemon-and-client-easily/
# This script will invoke emacsclient. If there is no emacs daemon
# running, then the daemon is automatically started.

# If any of -nw, -t or -tty, or for that matter -c or --create-frame,
# is specified on the command line, then the script will just pass on
# all arguments to emacsclient.

# But if none of that is specified, this script will assume that this
# is interactive usage. If $DISPLAY is set, the script assumes that
# the user would prefer to invoke the emacsclient with the --no-wait
# flag, opening the specified file (if any) in an existing frame (in
# the current $DISPLAY).  If there no frame currently open (in the
# current $DISPLAY) the script will add --create-frame as well.

# Because of --no-wait, it is not a good idea to let the EDITOR
# variable point to this script without command line flags. Suggested
# values for EDITOR would be "<script> --create-frame " or "<script>
# -nw"

# Tested with emacs 23.0.9 packaged for Ubuntu 8.10:
# $ emacs-snapshot --version
# GNU Emacs 23.0.90.1
# $ emacsclient.emacs-snapshot -V
# emacsclient 23.0.90

##############

#set -x # Print lots of debugging info

this_script="$0" 
  # Useful for prefixing printouts, and for self-invoking this script

  # This script can invoke itself by giving the --alternate-editor
  # option to emacsclient. The MUST_START_EMACS_DAEMON environment
  # variable is used to keep track on that:
if [ "x$MUST_START_EMACS_DAEMON" != "x" ] ; then
    echo "$this_script: Starting emacs --daemon. Ignoring all arguments: $@" >&2
    emacs-snapshot --daemon
      # No need to sleep or anything because the daemon will not fork
      # (return) until fully started.
    exit 1
      # We return an error code because starting the daemon implies a
      # previous failure to invoke the client. See usage below.
fi

  # Internal helper function that invokes the client, but if that
  # fails at least daemon is started before an error code is returned.
function _invoke_client_orelse_start_daemon() {
      # Note that stuff printed to stdout may/will be interpreted by
      # caller... So be careful with printouts -- use stderr:>&2
    local MUST_START_EMACS_DAEMON=1
    export MUST_START_EMACS_DAEMON
      # This script itself is specified as --alternate-editor, and it
      # will make sure to (try to) start the daemon at least. See
      # above.
    emacsclient.emacs-snapshot "$@" --alternate-editor $this_script
    retval=$?
    unset MUST_START_EMACS_DAEMON
    return $retval
}

  # Invokes the client. Also starts the daemon if necessary.
function _invoke_client(){
  _invoke_client_orelse_start_daemon "$@" ||
  emacsclient.emacs-snapshot "$@"
}

args=( "$@" )
  # Need to be careful to treat args as an *array*, so that command
  # line arguments are preserved until we pass them on -- even though
  # they may be containing spaces.. See `man bash` for more about
  # using parantheses for arrays, and about stuff like "$@" and
  # "${args[@]}"

if [ "x$DISPLAY" != "x" ] ; then
    user_has_specified_frame=false;
    for opt in "$@" ; do
	case $opt in 
	    -t)   user_has_specified_frame=true; break ;; # breaks out of the loop
	    -tty) user_has_specified_frame=true; break ;;
	    -nw)  user_has_specified_frame=true; break ;;
	    -c)   user_has_specified_frame=true; break ;;
	    --create-frame) user_has_specified_frame=true; break ;;
	esac
    done

    if [ $user_has_specified_frame == false ] ; then
          # Hmm, is this the only situation where we want to add
          # --no-wait?
        args=( "--no-wait" "${args[@]}" )

	display="\"$DISPLAY\"" 
          # display will contain something like "", ":0.0" or
          # "localhost:10.0" (including the quotes)
	current_emacs_displays=`_invoke_client_orelse_start_daemon --eval "(x-display-list)"` 
          # current_emacs_displays will contain something like (":0.0"
          # "localhost:10.0") current_emacs_displays may also be empty
          # if there was no daemon running, of if there is no frame
          # open.
	if [[ ! "$current_emacs_displays" =~ "$display" ]] ; then
              # If there is a match (display is included in
              # current_emacs_displays), then there is already a frame
              # open in this $DISPLAY. *Otherwise* we'll add the
              # --create-frame option to the array:
            args=( "--create-frame" "${args[@]}" )
	fi
    fi

fi # if DISPLAY

_invoke_client "${args[@]}"

# TODO: the "emacsclient || (emacsdaemon && emacsclient)" construct is
# ugly when the emacsclient call fails due to error in arguments --
# the daemon is invoked, but since it is already started a big error
# message is emitted -- FIXED -- now using the --alternate-editor
# mechanism, which works more gracefully.

# TODO: Check all if-statemenst for potentially empty values, for
# example "$current_emacs_displays" ..?
Advertisements


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s