Commit f858a901 authored by Lev Lamberov's avatar Lev Lamberov

New upstream version 0.1.1

parents
((emacs-lisp-mode
(compile-command . "cask exec ert-runner")
(eval ignore-errors
(push (quote ("Tests" "(\\(\\<ert-deftest\\)\\>\\s *\\(\\(?:\\sw\\|\\s_\\)+\\)?" 2)) imenu-generic-expression))))
--reporter ert
\ No newline at end of file
language: emacs-lisp
sudo: false
env:
- EVM_EMACS=emacs-24.5
- EVM_EMACS=emacs-24.4
- EVM_EMACS=emacs-24.3
- EVM_EMACS=emacs-24.2
- EVM_EMACS=emacs-24.1
cache:
apt: true
directories:
- $HOME/emacsen
addons:
apt:
packages:
- python
- curl
- libxpm-dev
- texinfo
- x11-utils
- xdotool
before_install:
- "export DISPLAY=:99.0"
- "sh -e /etc/init.d/xvfb start"
- sleep 3
- curl -fsSkL https://raw.github.com/rejeep/evm/master/go | bash
- export PATH="$HOME/.evm/bin:$PATH"
- mkdir -p $HOME/emacsen
- evm config path $HOME/emacsen
install:
- evm install $EVM_EMACS --skip
- evm use $EVM_EMACS
- curl -fsSL https://raw.githubusercontent.com/cask/cask/master/go | python
- export PATH="/home/travis/.cask/bin:$PATH"
- cask install
script:
- cask exec ert-runner
- sh linux-gui-restart-test.sh
notifications:
email:
recipients:
- iqbalansari02@yahoo.com
on_success: never
on_failure: always
\ No newline at end of file
(source gnu)
(source melpa)
(package-file "restart-emacs.el")
(files "*.el")
(development
(depends-on "f")
(depends-on "ecukes")
(depends-on "ert-runner")
(depends-on "el-mock"))
* v0.1.1
Bugfix release fixing issues on Emacs versions older than v24.3 (and greater
than v24.1)
** Do not wait for output from new instance of Emacs spawned in parent Emacs
** Signals `error` instead of `user-error` if the later is not available (Emacs older than v24.3)
** Add tests
* v0.1
Initial public release
This diff is collapsed.
* Restart Emacs
[[http://melpa.org/#/restart-emacs][file:http://melpa.org/packages/restart-emacs-badge.svg]] [[http://stable.melpa.org/#/restart-emacs][file:http://stable.melpa.org/packages/restart-emacs-badge.svg]] [[https://travis-ci.org/iqbalansari/restart-emacs][https://travis-ci.org/iqbalansari/restart-emacs.svg?branch=master]]
** Table of contents :TOC_3_gh:
- [[#restart-emacs][Restart Emacs]]
- [[#what-is-this][What is this?]]
- [[#installation][Installation]]
- [[#elpa][ELPA]]
- [[#el-get][El-get]]
- [[#usage][Usage]]
- [[#compatibility][Compatibility]]
- [[#restarting-gui-emacs][Restarting GUI Emacs]]
- [[#restarting-emacs-running-in-a-terminal][Restarting Emacs running in a terminal]]
** What is this?
This is a simple package to restart Emacs for within Emacs. Inspired by [[http://emacs.stackexchange.com/questions/5428/restart-emacs-from-within-emacs][this]]
stackoverflow question.
** Installation
*** ELPA
~restart-emacs~ is available on [[http://melpa.org/#/restart-emacs][MELPA]] and [[http://stable.melpa.org/#/restart-emacs][MELPA Stable]]. Please follow the instructions on
MELPA [[http://melpa.org/#/getting-started][website]] to enable it, if you haven't already.
You can then install ~restart-emacs~ from the [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Package-Menu.html][package menu]]. Alternatively install it by doing the following
Refresh the package index
#+BEGIN_QUOTE
M-x package-refresh-contents RET
#+END_QUOTE
And then install it by doing
#+BEGIN_QUOTE
M-x package-install RET restart-emacs
#+END_QUOTE
*** El-get
~restart-emacs~ can also be installed using ~el-get~. Assuming you have latest version of el-get installing it by doing something similar to
#+BEGIN_QUOTE
M-x el-get-install RET restart-emacs
#+END_QUOTE
** Usage
It offers only one command ~restart-emacs~ which kills current Emacs session
and starts a new session.
Additional arguments to be passed to the new instance can be specified using
prefix arguments
- with a single ~universal-argument~ (=C-u=) Emacs is restarted with ~--debug-init~ flag
- with two ~universal-argument~ (=C-u= =C-u=) Emacs is restarted with ~-Q~ flag
- with three ~universal-argument~ (=C-u= =C-u= =C-u=) the user is prompted for the arguments
** Compatibility
*** Restarting GUI Emacs
Restarting graphical Emacs should work on any UNIXy system with ~sh~ and on
Windows.
*** Restarting Emacs running in a terminal
This requires that the shell from which Emacs was launched supports job
control. This has been tested to work on ~sh~, ~bash~, ~zsh~, ~fish~, ~csh~
and ~tcsh~, however this does not work on Windows.
#!/bin/sh
# This scripts tests that Emacs restarts successfully under X
# It requires the following utilities installed
# xprop - Available in x11-utils package on Ubuntu
# xwininfo - Available in x11-utils package on Ubuntu
# xdotool - Available in xdotool package on Ubuntu
if [ -z "$DISPLAY" ] ; then
echo "This script works only under X!"
echo "Exiting ..."
exit 2
fi
if [ -z $(which xprop 2> /dev/null) ] || [ -z $(which xwininfo 2> /dev/null) ] || [ -z $(which xdotool 2> /dev/null) ] ; then
echo "This script requires xprop, xwininfo and xdotool to work."
echo "If you are on Ubuntu, please install x11-utils and xdotools packages to get these tools."
echo "Exiting ..."
exit 2
fi
poll_emacs_started () {
for i in $(seq 1 10) ; do
echo "Checking if Emacs started ... "
if xwininfo -name $EMACS_WIN_NAME > /dev/null 2>&1 ; then
echo "Emacs has started ... "
return 0
elif [ $i = 10 ] ; then
echo "Giving up after 10 tries ... "
return 1
fi
echo "Emacs hasn't started yet! Will try again in 5 seconds ... "
sleep 5
done
}
# So that user config does not interfere with functioning of test Emacs
HOME=/tmp
SUCCESS=1
# Name that should be given Emacs window, makes easier to search for window
EMACS_WIN_NAME=restart-emacs-test
# Setup things so that Emacs window is renamed appropriately after starting
mkdir -p /tmp/.emacs.d/
printf "(modify-frame-parameters nil (list (cons 'name \"$EMACS_WIN_NAME\")))" > /tmp/.emacs.d/init.el
# Launch Emacs
cask exec emacs -l restart-emacs.el > /dev/null 2>&1 &
if poll_emacs_started ; then
EMACS_WINDOW_ID=$(xwininfo -name $EMACS_WIN_NAME | grep 'Window id:' | awk -F' ' '{print $4}')
ORIG_EMACS_PID=$(xprop -name $EMACS_WIN_NAME | grep PID | awk -F' = ' '{print $2}')
echo "Found Emacs running with window id $EMACS_WINDOW_ID and pid $ORIG_EMACS_PID .. "
sleep 1
echo "Asking Emacs to restart .. "
xdotool search --name $EMACS_WIN_NAME key alt+x
sleep 1
xdotool search --name $EMACS_WIN_NAME type 'restart-emacs'
sleep 1
xdotool search --name $EMACS_WIN_NAME key Return
sleep 5
if poll_emacs_started ; then
RESTARTED_EMACS_PID=$(xprop -name $EMACS_WIN_NAME | grep PID | awk -F' = ' '{print $2}')
if [ $ORIG_EMACS_PID != $RESTARTED_EMACS_PID ] ; then
echo "Successfully restarted Emacs!"
echo "Test PASSED"
SUCCESS=0
else
echo "The original Emacs was not killed properly!"
echo "Test FAILED"
fi
else
echo "Could not restart Emacs, after killing original one!"
echo "Test FAILED"
fi
else
echo "Could start Emacs, please check for errros and try again!"
SUCCESS=2
fi
# Cleanup
xdotool search --name $EMACS_WIN_NAME key ctrl+x
xdotool search --name $EMACS_WIN_NAME key ctrl+c
exit $SUCCESS
;;; restart-emacs.el --- Restart emacs from within emacs -*- lexical-binding: t; -*-
;; Copyright (C) 2015 Iqbal Ansari
;; Author: Iqbal Ansari <iqbalansari02@yahoo.com>
;; Keywords: convenience
;; URL: https://github.com/iqbalansari/restart-emacs
;; Version: 0.1.1
;; 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 3 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, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; This package provides a simple command to restart Emacs from within Emacs
;;; Code:
;; Making the byte compiler happy
(declare-function w32-shell-execute "w32fns.c")
;; Compatibility functions
(defun restart-emacs--string-join (strings &optional separator)
"Join all STRINGS using SEPARATOR.
This function is available on Emacs v24.4 and higher, it has been
backported here for compatibility with older Emacsen."
(if (fboundp 'string-join)
(apply #'string-join (list strings separator))
(mapconcat 'identity strings separator)))
(defun restart-emacs--user-error (format &rest args)
"Signal a `user-error' if available otherwise signal a generic `error'.
FORMAT and ARGS correspond to STRING and OBJECTS arguments to `format'."
(if (fboundp 'user-error)
(apply #'user-error format args)
(apply #'error format args)))
;; Core functions
(defvar restart-emacs--args nil
"The arguments with which to restart Emacs is bound dynamically.")
(defun restart-emacs--get-emacs-binary ()
"Get absolute path to binary of currently running Emacs."
(expand-file-name invocation-name invocation-directory))
(defun restart-emacs--start-gui-using-sh (&optional args)
"Start GUI version of Emacs using sh.
ARGS is the list arguments with which Emacs should be started"
(call-process "sh" nil
0 nil
"-c" (format "%s %s &"
(shell-quote-argument (restart-emacs--get-emacs-binary))
(restart-emacs--string-join (mapcar #'shell-quote-argument
args)
" "))))
(defun restart-emacs--start-gui-on-windows (&optional args)
"Start GUI version of Emacs on windows.
ARGS is the list arguments with which Emacs should be started"
(w32-shell-execute "open" (restart-emacs--get-emacs-binary) args))
(defun restart-emacs--start-emacs-in-terminal (&optional args)
"Start Emacs in current terminal.
ARGS is the list arguments with which Emacs should be started. This requires a
shell with `fg' command and `;' construct. This has been tested to work with
sh, bash, zsh, fish, csh and tcsh shells"
(suspend-emacs (format "fg ; %s %s -nw"
(shell-quote-argument (restart-emacs--get-emacs-binary))
(restart-emacs--string-join (mapcar #'shell-quote-argument
args)
" "))))
(defun restart-emacs--ensure-can-restart ()
"Ensure we can restart Emacs on current platform."
(when (and (not (display-graphic-p))
(memq system-type '(windows-nt ms-dos)))
(restart-emacs--user-error (format "Cannot restart emacs running in terminal on system of type `%s'" system-type))))
(defun restart-emacs--launch-other-emacs ()
"Launch another Emacs session according to current platform."
(apply (if (display-graphic-p)
(if (memq system-type '(windows-nt ms-dos))
#'restart-emacs--start-gui-on-windows
#'restart-emacs--start-gui-using-sh)
(if (memq system-type '(windows-nt ms-dos))
;; This should not happen since we check this before triggering a restart
(restart-emacs--user-error "Cannot restart Emacs running in a windows terminal")
#'restart-emacs--start-emacs-in-terminal))
;; Since this function is called in `kill-emacs-hook' it cannot accept
;; direct arguments the arguments are let-bound instead
(list restart-emacs--args)))
(defun restart-emacs--translate-prefix-to-args (prefix)
"Translate the given PREFIX to arguments to be passed to Emacs.
It does the following translation
`C-u' => --debug-init
`C-u' `C-u' => -Q
`C-u' `C-u' `C-u' => Reads the argument from the user in raw form"
(cond ((equal prefix '(4)) '("--debug-init"))
((equal prefix '(16)) '("-Q"))
((equal prefix '(64)) (split-string (read-string "Arguments to start Emacs with (separated by space): ")
" "))))
;; User interface
;;;###autoload
(defun restart-emacs (&optional args)
"Restart Emacs.
When called interactively ARGS is interpreted as follows
- with a single `universal-argument' (`C-u') Emacs is restarted
with `--debug-init' flag
- with two `universal-argument' (`C-u') Emacs is restarted with
`-Q' flag
- with three `universal-argument' (`C-u') the user prompted for
the arguments
When called non-interactively ARGS should be a list of arguments
with which Emacs should be restarted."
(interactive "P")
;; Do not trigger a restart unless we are sure, we can restart emacs
(restart-emacs--ensure-can-restart)
;; We need the new emacs to be spawned after all kill-emacs-hooks
;; have been processed and there is nothing interesting left
(let ((kill-emacs-hook (append kill-emacs-hook (list #'restart-emacs--launch-other-emacs)))
(restart-emacs--args (if (called-interactively-p 'any)
(restart-emacs--translate-prefix-to-args args)
args)))
(save-buffers-kill-emacs)))
(provide 'restart-emacs)
;;; restart-emacs.el ends here
;;; restart-emacs-test.el --- Tests for restart-emacs -*- lexical-binding: t; -*-
;;; Commentary:
;;; Tests for restart-emacs, major use case is to run from the command-line
;;; using `cask exec ert-runner' but can be run interactively as well. Do
;;; M-x `eval-buffer' RET
;;; Code:
;; For interactive testing
(unless noninteractive
(load (expand-file-name "test-helper.el") t))
(require 'restart-emacs)
(require 'ert)
(require 'el-mock)
;; el-mock requires this
(require 'cl)
(require 'cl-lib)
(ert-deftest restart-emacs-test-prefix-translation ()
(with-mock
(stub read-string => "read string")
(should (equal (restart-emacs--translate-prefix-to-args '(4)) '("--debug-init")))
(should (equal (restart-emacs--translate-prefix-to-args '(16)) '("-Q")))
(should (equal (restart-emacs--translate-prefix-to-args '(64)) '("read" "string")))))
(ert-deftest restart-emacs-test-restart-capability ()
(with-mock
(stub display-graphic-p => nil)
;; `signal' tries to use this (and fails) if `system-type' is `windows-nt',
;; stub it out
(stub w32-long-file-name => "/tmp")
(let ((system-type 'windows-nt))
(should-error (restart-emacs--ensure-can-restart) :type (if (version<= "24.3" emacs-version)
'user-error
'error)))
(let ((system-type 'ms-dos))
(should-error (restart-emacs--ensure-can-restart) :type (if (version<= "24.3" emacs-version)
'user-error
'error))))
(with-mock
(stub display-graphic-p => t)
(let ((system-type 'windows-nt))
(should-not (restart-emacs--ensure-can-restart)))
(let ((system-type 'ms-dos))
(should-not (restart-emacs--ensure-can-restart)))))
(ert-deftest restart-emacs-test-path-calculation ()
(let ((invocation-name "emacs")
(invocation-directory "/tmp/test/"))
(should (string= (restart-emacs--get-emacs-binary) "/tmp/test/emacs"))))
(ert-deftest restart-emacs-test-gui-using-sh ()
(with-mock
(stub restart-emacs--get-emacs-binary => "/tmp/bin/emacs")
(mock (call-process "sh" nil
0 nil
"-c" "/tmp/bin/emacs --debug-init &"))
(should-not (restart-emacs--start-gui-using-sh '("--debug-init")))))
(ert-deftest restart-emacs-test-gui-on-windows ()
(with-mock
(stub restart-emacs--get-emacs-binary => "/tmp/bin/emacs")
(mock (w32-shell-execute "open" "/tmp/bin/emacs" '("--debug-init")))
(should-not (restart-emacs--start-gui-on-windows '("--debug-init")))))
(ert-deftest restart-emacs-test-start-emacs-in-terminal ()
(with-mock
(stub restart-emacs--get-emacs-binary => "/tmp/bin/emacs")
(mock (suspend-emacs "fg ; /tmp/bin/emacs --debug-init -nw"))
(should-not (restart-emacs--start-emacs-in-terminal '("--debug-init")))))
(ert-deftest restart-emacs-test-strategy ()
(with-mock
(stub display-graphic-p => nil)
;; `signal' tries to use this (and fails) if `system-type' is `windows-nt',
;; stub it out
(stub w32-long-file-name => "/tmp")
(mock (restart-emacs--start-emacs-in-terminal nil) :times 5)
(dolist (system '(gnu gnu/linux gnu/kfreebsd darwin cygwin))
(let ((system-type system))
(should-not (restart-emacs--launch-other-emacs))))
(dolist (system '(windows-nt ms-dos))
(let ((system-type system))
(should-error (restart-emacs--launch-other-emacs) :type (if (version<= "24.3" emacs-version)
'user-error
'error)))))
(with-mock
(stub display-graphic-p => t)
(mock (restart-emacs--start-gui-using-sh nil) :times 5)
(mock (restart-emacs--start-gui-on-windows nil) :times 2)
(dolist (system '(gnu gnu/linux gnu/kfreebsd darwin cygwin))
(let ((system-type system))
(should-not (restart-emacs--launch-other-emacs))))
(dolist (system '(windows-nt ms-dos))
(let ((system-type system))
(should-not (restart-emacs--launch-other-emacs))))))
(ert-deftest restart-emacs-test-restart-setup ()
(cl-letf (((symbol-function 'save-buffers-kill-emacs)
(lambda nil
(should (equal (cl-position #'restart-emacs--launch-other-emacs kill-emacs-hook)
(1- (length kill-emacs-hook)))))))
(restart-emacs)
;; Make sure that hook to restart emacs is removed after the function
;; restart-emacs exits
(should-not (cl-find #'restart-emacs--launch-other-emacs kill-emacs-hook))))
(unless noninteractive
(ert "^restart-emacs-"))
(provide 'restart-emacs-test)
;;; restart-emacs-test.el ends here
;;; test-helper.el --- Tests for restart-emacs -*- lexical-binding: t; -*-
;;; Commentary:
;;; Helpers to write tests for restart-emacs
;;; Code:
;; Setup load-path, some of this is redundant when tests are run from the
;; command line
(let ((project-dir (locate-dominating-file (or (buffer-file-name) load-file-name)
".cask")))
(if (not project-dir)
(user-error "Could not locate project root")
(let ((default-directory (expand-file-name (concat ".cask/" emacs-version) project-dir)))
(normal-top-level-add-subdirs-to-load-path))
(add-to-list 'load-path project-dir)))
(provide 'test-helper)
;;; test-helper.el ends here
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment