summaryrefslogtreecommitdiff
path: root/restart-emacs.el
diff options
context:
space:
mode:
Diffstat (limited to 'restart-emacs.el')
-rw-r--r--restart-emacs.el159
1 files changed, 159 insertions, 0 deletions
diff --git a/restart-emacs.el b/restart-emacs.el
new file mode 100644
index 0000000..c5e4f8d
--- /dev/null
+++ b/restart-emacs.el
@@ -0,0 +1,159 @@
+;;; 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