My hack on shell-toggle

One of my daily task in Emacs is to compose LaTeX. Considering the LaTeX->BibTeX->LaTeX->LaTeX building cycle of a large, complex, reference-rich tex file, using make is a obvious solution. As I do enjoy watching the LaTeX programming running on my tex file and keep generating outputs, I’d like to use a shell, preferably available in Emacs, to trigger the make command. Thanks to the great Mikael Sjödin’s shell-toggle.el (, this task becomes pretty easy: when you are working on your buffer, and if you want to jump to a shell buffer at your current buffer’s directory, just simply C-‘, then the window will split and a shell buffer will be opened at your buffer’s directory. Hit C-‘ again, the shell buffer will be maximized. Hit C-‘ again, your original buffer will be restored to its maximized state.

My hack on this shell-toggle.el is to enable the shell-toggle-cd to insert a "cd <file-dir-of-current-buffer>" to the shell and to execute it. This is pretty convenient when you are working on multiple buffers, and your shell is at a directory different from your current buffer’s. It is pretty much a hassle to type "cd XXXXXX" to change directory. With my hack to shell-toggle.el you can just C-M-‘ (or M-x shell-toggle-cd), then a cd command to change dir to your current buffer’s path will be sent to the shell buffer, and your shell’s dir is changed automatically for you. The code in my .emacs file is as follows:

(autoload 'shell-toggle "da-shell-toggle"
"Toggles between the *shell* buffer and whatever buffer you are editing."
(autoload 'shell-toggle-cd "da-shell-toggle"
"Pops up a shell-buffer and insert a \"cd <file-dir>\" command." t)

(global-set-key (kbd "C-M-'") 'shell-toggle-cd)
(global-set-key (kbd "C-'") 'shell-toggle)
(global-set-key (kbd "C-M-!") 'shell-command)

And my hack on the shell-toggle.el is as follows:

;;; da-shell-toggle.el
;;; Da Zhang's hack to shell-toggle.el
;;; Many thanks to Mikael Sjödin for shell-toggle.el

;;; shell-toggle.el--- Toggle to and from the *shell* buffer
;;; Version 1.2 - 98-11-19
;;; Copyright (C) 1997, 1998 Mikael Sjödin (
;;; Author: Mikael Sjödin --
;;; This file is NOT part of GNU Emacs.
;;; You may however 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, or (at your option) any later version.
;;; The file is distributed in the hope that it will be useful,
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;; GNU General Public License for more details.

;;; ----------------------------------------------------------------------
;;; Description:
;;; Provides the command shell-toggle which toggles between the
;;; *shell* buffer and whatever buffer you are editing.
;;; This is done in an "intelligent" way. Features are:
;;; o Starts a shell if non is existing.
;;; o Minimum distortion of your window configuration.
;;; o When done in the shell-buffer you are returned to the same window
;;; configuration you had before you toggled to the shell.
;;; o If you desire, you automagically get a "cd" command in the shell to the
;;; directory where your current buffers file exists; just call
;;; shell-toggle-cd instead of shell-toggle.
;;; o You can convinently choose if you want to have the shell in another
;;; window or in the whole frame. Just invoke shell-toggle again to get the
;;; shell in the whole frame.
;;; This file has been tested under Emacs 20.2.
;;; This file can be obtained from

;;; ----------------------------------------------------------------------
;;; Installation:
;;; o Place this file in a directory in your 'load-path.
;;; o Put the following in your .emacs file:
;;; (autoload 'shell-toggle "shell-toggle"
;;; "Toggles between the *shell* buffer and whatever buffer you are editing."
;;; t)
;;; (autoload 'shell-toggle-cd "shell-toggle"
;;; "Pops up a shell-buffer and insert a \"cd <file-dir>\" command." t)
;;; (global-set-key [M-f1] 'shell-toggle)
;;; (global-set-key [C-f1] 'shell-toggle-cd)
;;; o Restart your Emacs. To use shell-toggle just hit M-f1 or C-f1
;;; For a list of user options look in code below.

;;; ----------------------------------------------------------------------
;;; BUGS:
;;; No reported bugs as of today

;;; ----------------------------------------------------------------------
;;; Thanks to:
;;; Christian Stern <> for helpful
;;; sugestions.

;;; ======================================================================
;;; User Options:

(defvar shell-toggle-goto-eob t
"*If non-nil `shell-toggle' will move point to the end of the shell-buffer
whenever the `shell-toggle' switched to the shell-buffer.

When `shell-toggle-cd' is called the point is allways moved to the end of the

(defvar shell-toggle-automatic-cd t
"*If non-nil `shell-toggle-cd' will send the \"cd\" command to the shell.
If nil `shell-toggle-cd' will only insert the \"cd\" command in the
shell-buffer. Leaving it to the user to press RET to send the command to
the shell.")

;;; ======================================================================
;;; Commands:

(defun shell-toggle-cd ()
"Calls `shell-toggle' with a prefix argument. Se command `shell-toggle'"
(shell-toggle t))

(defun shell-toggle (make-cd)
"Toggles between the *shell* buffer and whatever buffer you are editing.
With a prefix ARG also insert a \"cd DIR\" command into the shell, where DIR is
the directory of the current buffer.

Call twice in a row to get a full screen window for the *shell* buffer.

When called in the *shell* buffer returns you to the buffer you were editing
before caling the first time.

Options: `shell-toggle-goto-eob'"
(interactive "P")
;; Try to descide on one of three possibilities:
;; If not in shell-buffer, switch to it.
;; If in shell-buffer and called twice in a row, delete other windows
;; If in shell-buffer and not called twice in a row, return to state before
;; going to the shell-buffer
(if (eq major-mode 'shell-mode)
(if (and (or (eq last-command 'shell-toggle)
(eq last-command 'shell-toggle-cd))
(not (eq (count-windows) 1)))
;; (shell-toggle-buffer-goto-shell t) Da Zhang's customization
(shell-toggle-buffer-goto-shell make-cd)))

;;; ======================================================================
;;; Internal functions and declarations

(defvar shell-toggle-pre-shell-win-conf nil
"Contains the window configuration before the *shell* buffer was selected")

(defun shell-toggle-buffer-return-from-shell ()
"Restores the window configuration used before switching the *shell* buffer.
If no configuration has been stored, just burry the *shell* buffer."
(if (window-configuration-p shell-toggle-pre-shell-win-conf)
(set-window-configuration shell-toggle-pre-shell-win-conf)
(setq shell-toggle-pre-shell-win-conf nil)
(bury-buffer (get-buffer "*shell*")))

(defun shell-toggle-buffer-goto-shell (make-cd)
"Switches other window to the *shell* buffer. If no *shell* buffer exists
start a new shell and switch to it in other window. If argument MAKE-CD is
non-nil, insert a \"cd DIR\" command into the shell, where DIR is the directory
of the current buffer.

Stores the window cofiguration before creating and/or switching window."
(setq shell-toggle-pre-shell-win-conf (current-window-configuration))
(let ((shell-buffer (get-buffer "*shell*"))
;; Find out which directory we are in (the method differs for
;; different buffers)
(or (and make-cd
(file-name-directory (buffer-file-name))
(concat "cd \"" (file-name-directory (buffer-file-name)) "\""));; Da Zhang's customization, adding \"
(and make-cd
(concat "cd \"" list-buffers-directory "\"")))))

;; Switch to an existin shell if one exists, otherwise switch to another
;; window and start a new shell
(if shell-buffer
(switch-to-buffer-other-window shell-buffer)
;; Sometimes an error is generated when I call `shell'
;; (it has to do with my shell-mode-hook which inserts text into the
;; newly created shell-buffer and thats not allways a good idea).
(condition-case the-error
(error (switch-to-buffer "*shell*"))))
(if (or cd-command shell-toggle-goto-eob)
(goto-char (point-max)))
(if cd-command
(insert cd-command)
(if shell-toggle-automatic-cd

(defun shell-toggle-buffer-switch-to-other-window ()
"Switches to other window. If the current window is the only window in the
current frame, create a new window and switch to it.

\(This is less intrusive to the current window configuration than
(let ((this-window (selected-window)))
(other-window 1)
;; If we did not switch window then we only have one window and need to
;; create a new one.
(if (eq this-window (selected-window))
(other-window 1)))))

(provide 'shell-toggle)


One thought on “My hack on shell-toggle

Leave a Reply

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

You are commenting using your 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