-;;; multishell.el --- facilitate use of multiple local and remote shell buffers
+;;; multishell.el --- facilitate multiple local and remote shell buffers
;; Copyright (C) 1999-2016 Free Software Foundation, Inc. and Ken Manheimer
;; a la `pop-to-buffer' - plus a keybinding. Together, they enable you to:
;;
;; * Get to the input point from wherever you are in a shell buffer,
-;; * ... or to one of your shell buffers if you're not currently in one.
+;; ... or to any of your shell buffers, from elsewhere inside emacs.
+;;
;; * Use universal arguments to launch and choose among alternate shell buffers,
-;; * ... and select which is default.
-;; * Append a path to a new shell name to launch a shell in that directory,
-;; * ... and use a path with Emacs tramp syntax to launch a remote shell.
+;; * ... and change which is the current default.
;;
-;; For example:
+;; * Easily restart disconnected shells, or shells from prior sessions
+;; * ... the latter from Emacs builtin savehist minibuf history persistence
+;;
+;; * Append a path to a new shell name to launch a shell in that directory,
+;; * ... and use a path with Emacs tramp syntax to launch a remote shell -
+;; for example:
;;
-;; * `/ssh:example.net:/` for a shell buffer in / on
-;; example.net; the buffer will be named "*example.net*".
+;; * `/ssh:example.net:/` for a shell buffer in / on example.net.
+;; The buffer will be named "*example.net*".
;;
;; * `#ex/ssh:example.net|sudo:root@example.net:/etc` for a root shell
;; starting in /etc on example.net named "*#ex*".
;;
-;; (NOTE - there's a sporadic problem when opening a shell pointed at a
-;; remote homedir, eg `/ssh:example.net:` or `/ssh:example.net:~`. It
-;; sometimes fails, particularly for remote+sudo with homedir syntax. Until
-;; fixed, you may need to start remote+sudo shells with an explicit path,
-;; then cd ~. If you set up `multishell`s persistent dir-tracking history,
-;; you'll be able to use completion to start that shell in the right place,
-;; in your subsequent sessions.)
+;; * '\#intrn/ssh:corp.com|ssh:intern.corp.com|sudo:root@intern.corp.com:/etc'
+;; to go via corp.com to intern.corp.com, sudood to root, in /etc. Whee! (-:
+;; The buffer will be named "*#intrn*".
;;
+;; * File visits will all be under the auspices of the account, and relative to
+;; the current directory, on the remote host.
+;;
;; See the `multishell-pop-to-shell` docstring for details.
;;
;; Customize-group `multishell' to select and activate a keybinding and set
;; various behaviors. Customize-group `savehist' to preserve buffer
;; names/paths across emacs sessions.
;;
-;; See the `multishell-pop-to-shell' docstring for details.
-;;
-;; Please use [the repository](https://github.com/kenmanheimer/EmacsMultishell)
+;; Please use
+;; [the multishell repository](https://github.com/kenmanheimer/EmacsMultishell)
;; issue tracker to report problems, suggestions, etc.
;;
-;;; Change Log:
+;; (NOTE - tramp sometimes has a problem opening a remote shell pointed at
+;; a homedir, eg `/ssh:example.net:` or `/ssh:example.net:~`. When it
+;; fails, it won't work for the rest of the session. Non-homedir remote
+;; access isn't disrupted. Until this is fixed, you may need to start
+;; remote shells with an explicit path, then cd ~.)
+;;
+;; Change Log:
;;
;; * 2016-01-16 1.0.5 Ken Manheimer:
;; - History now includes paths, when designated
;; - Actively track current directory in history entries that have a path.
;; Custom control: multishell-history-entry-tracks-current-directory
-;; - Offer to remove shell's history entry when buffer is killed
-;; (For now, the only UI way to remove history entries.)
+;; - Offer to remove shell's history entry when buffer is killed.
+;; (Currently the only UI mechanism to remove history entries.)
;; - Fix - prevent duplicate entries for same name but different paths
;; - Fix - recognize and respect tramp path syntax to start in home dir
-;; - But tramp bug, remote+sudo hops to a home dir can fail, get wedged.
+;; - But tramp bug, remote w/empty path (homedir) often fails, gets wedged.
;; - Simplify history var name, migrate existing history if any from old name
-;; * 2016-01-04 Ken Manheimer - Released to ELPA
+;; * 2016-01-04 1.0.4 Ken Manheimer - Released to ELPA
;; * 2016-01-02 Ken Manheimer - working on this in public, but not yet released.
;;
-;;; TODO:
+;; TODO:
;;
-;; * Isolate tramp sporadic failure to connect to remote+sudo+homedir syntax
+;; * Isolate tramp sporadic failure to connect to remote+homedir (empty path)
+;; syntax
;; (eg, /ssh:xyz.com|sudo:root@xyz.com: or /ssh:xyz.com|sudo:root@xyz.com:~)
;; * Find suitable, internally consistent ways to sort tidy completions, eg:
;; - first list completions for active shells, then present but inactive,
(defvar multishell-primary-name "*shell*"
"Shell name to use for un-modified multishell-pop-to-shell buffer target.")
-;; There is usually only one entry per name, but disruptions happen.
+;; Multiple entries happen because completion also adds name to history.
(defun multishell-register-name-to-path (name path)
"Add or replace entry associating NAME with PATH in `multishell-history'.
-Promote to added/changed entry to the front of the list."
+If NAME already had a PATH and new PATH is empty, retain old one.
+
+Promote added/changed entry to the front of the list."
;; Add or promote to the front, tracking path changes in the process.
(let* ((entries (multishell-history-entries name))
- (becomes (concat name path)))
+ (becomes (concat name path))
+ oldpath)
(dolist (entry entries)
+ (when (or (not path) (string= path ""))
+ ;; Retain explicit established path.
+ (setq path (cadr (multishell-split-entry-name-and-tramp entry))
+ becomes (concat name path)))
(setq multishell-history (delete entry multishell-history)))
(setq multishell-history (push becomes multishell-history))))
\"*example.net*\".
* '\#ex/ssh:example.net|sudo:root@example.net:/etc' for a root
shell in /etc on example.net named \"*#ex*\".
+* '\#in/ssh:corp.com|ssh:internal.corp.com|sudo:root@internal.corp.com:/etc'
+ for a root shell name \"*in*\" in /etc on internal.corp.com, via host
+ corp.com.
\(NOTE that there is a problem with specifying a remote homedir using
tramp syntax, eg '/ssh:example.net:'. That sometimes fails on an obscure
-bug - particularly for remote+sudo with homedir syntax. Until fixed, you
-may need to start remote+sudo shells with an explicit path, then cd ~.)
+bug - particularly for remote with empty path (homedir) syntax. Until fixed,
+you may need to start remote shells with an explicit path, then cd ~.)
You can change the startup path for a shell buffer by editing it
at the completion prompt. The new path will be preserved in
To remove a shell buffer's history entry, kill the buffer and
affirm removal of the entry when prompted.
-===== Activate savehist to persisting your shell buffer names and paths:
+===== Activate savehist to retain shell buffer names and paths across Emacs sessions:
-To have emacs maintain your history of shell buffer names and paths,
+To have emacs maintain your history of shell buffer names and paths,
customize the savehist group to activate savehist."
(interactive "P")
"Selecting deleted buffer"))
(signal (car err)
(list
- (format "Tramp shell can fail on homedir paths, %s (\"%s\")"
- "please try with an explicit path"
+ (format "%s, %s (\"%s\")"
+ "Tramp shell can fail on empty (homedir) path"
+ "please try again with an explicit path"
(cadr err)))))))
(setq buffer (set-buffer (apply 'make-comint
(multishell-unbracket-asterisks buffer-name)