Add shell-mode bookmark support for local and remote shells (bug#65039)
* doc/emacs/misc.texi (Shell): Add "Shell Bookmarks" menu item. (Shell Mode): Fix typo. (Shell Bookmarks): New node. * etc/NEWS: Announce shell-mode bookmark capability. * lisp/bookmark.el: (bookmark-insert): Refuse to insert bookmarks whose handlers have the property 'bookmark-inhibit eq 'insert. * lisp/shell.el (shell-mode): Set bookmark-make-record-function. (shell-bookmark-name-function): New defcustom. (shell-bookmark-name-from-default-directory): New defun. (shell-bookmark-name-from-buffer-name): New defun. (shell-bookmark-defaults-function): New defvar. (shell-bookmark-defaults): New defun. (shell-bookmark-make-record): New defun. (shell-bookmark-jump-non-essential): New defvar. (shell-bookmark-jump): New defun with properties: 'bookmark-handler-type "Shell", 'bookmark-inhibit 'insert.
This commit is contained in:
parent
bf027eb6ff
commit
c1ddf612d5
4 changed files with 198 additions and 9 deletions
|
@ -773,6 +773,7 @@ See the Eshell Info manual, which is distributed with Emacs.
|
|||
* Shell Prompts:: Two ways to recognize shell prompts.
|
||||
* History: Shell History. Repeating previous commands in a shell buffer.
|
||||
* Directory Tracking:: Keeping track when the subshell changes directory.
|
||||
* Shell Bookmarks:: Save and restore local and remote shell buffers.
|
||||
* Options: Shell Options. Options for customizing Shell mode.
|
||||
* Terminal emulator:: An Emacs window as a terminal emulator.
|
||||
* Term Mode:: Special Emacs commands used in Term mode.
|
||||
|
@ -1199,7 +1200,7 @@ subshell:
|
|||
|
||||
By default, Shell mode handles common @acronym{ANSI} escape codes (for
|
||||
instance, for changing the color of text). Emacs also optionally
|
||||
supports some extend escape codes, like some of the @acronym{OSC}
|
||||
supports some extended escape codes, like some of the @acronym{OSC}
|
||||
(Operating System Codes) if you put the following in your init file:
|
||||
|
||||
@lisp
|
||||
|
@ -1503,6 +1504,51 @@ working directory; see the documentation of the variable
|
|||
dirtrack-mode} in the Shell buffer, or add @code{dirtrack-mode} to
|
||||
@code{shell-mode-hook} (@pxref{Hooks}).
|
||||
|
||||
@node Shell Bookmarks
|
||||
@subsection Shell Bookmarks
|
||||
@cindex shell bookmarks
|
||||
|
||||
Shell mode buffers can be bookmarked, and both local and remote
|
||||
(@pxref{Remote Files}) shell buffers are supported. @xref{Bookmarks}.
|
||||
|
||||
Opening, or ``jumping'' to, a bookmarked shell restores its buffer
|
||||
name, its current directory, and will create a remote connection, as
|
||||
necessary, using the shell command you used to create the remote buffer.
|
||||
|
||||
@vindex shell-bookmark-name-function
|
||||
@findex shell-bookmark-name-from-default-directory
|
||||
@findex shell-bookmark-name-from-buffer-name
|
||||
The option @code{shell-bookmark-name-function} can be customized to
|
||||
suit your preferences. It defaults to the function
|
||||
@code{shell-bookmark-name-from-default-directory} which uses the final
|
||||
component of the buffer's @code{default-directory}. An alternate
|
||||
function, @code{shell-bookmark-name-from-buffer-name}, uses the buffer's
|
||||
name with its @code{rename-uniquely} suffix brackets "<>" stripped. You
|
||||
can bind this option to your own function.
|
||||
|
||||
You can inhibit remote connections when you open a remote shell
|
||||
bookmark. This is useful when you restore sessions with
|
||||
@code{desktop-load}, or via another session-management package, to avoid
|
||||
time delays establishing connections. You can establish a connection on
|
||||
an unconnected remote buffer using the command @kbd{C-x C-v}
|
||||
(@code{find-alternate-file}). To inhibit a connection interactively,
|
||||
give a prefix argument before invoking the open/jump bookmark menu item,
|
||||
or before invoking the command @code{bookmark-jump}. @footnote{To
|
||||
inhibit a connection programmatically, refer to the documentation for
|
||||
the variable @code{shell-bookmark-jump-non-essential}.} @footnote{To
|
||||
properly handle multi-hop remote connections, refer to the documentation
|
||||
for the function @code{shell-bookmark-jump}.}
|
||||
|
||||
Note: Before creating ad-hoc multi-hop remote connections, customize
|
||||
either or both:
|
||||
@code{tramp-save-ad-hoc-proxies} to non-@code{nil} to persist proxy
|
||||
routes.
|
||||
@code{tramp-show-ad-hoc-proxies} to non-@code{nil} to ensure connections
|
||||
are fully qualified. This is helpful if you use the same persisted
|
||||
bookmarks file on multiple hosts.
|
||||
|
||||
@xref{Top, The Tramp Manual,, tramp, The Tramp Manual}.
|
||||
|
||||
@node Shell Options
|
||||
@subsection Shell Mode Options
|
||||
|
||||
|
|
10
etc/NEWS
10
etc/NEWS
|
@ -690,6 +690,16 @@ It removes all the buttons in the specified region.
|
|||
|
||||
** Shell
|
||||
|
||||
---
|
||||
*** Shell buffers now support bookmarks.
|
||||
|
||||
You can now bookmark local and remote shell buffers using the bookmark
|
||||
menu 'bookmark-bmenu-list', or by using the command 'bookmark-set'.
|
||||
Shell bookmarks can be loaded via the menu and by using the command
|
||||
'bookmark-jump', which open a bookmarked shell, restore its buffer name,
|
||||
its current directory, and create a remote connection, if necessary.
|
||||
You can customize 'shell-bookmark-name-function'.
|
||||
|
||||
*** New command to complete the shell history.
|
||||
'comint-complete-input-ring' ('C-x <up>') is like 'minibuffer-complete-history'
|
||||
but completes on comint inputs.
|
||||
|
|
|
@ -1530,14 +1530,18 @@ this."
|
|||
(interactive (list (bookmark-completing-read "Insert bookmark contents")))
|
||||
(bookmark-maybe-historicize-string bookmark-name)
|
||||
(bookmark-maybe-load-default-file)
|
||||
(let ((orig-point (point))
|
||||
(str-to-insert
|
||||
(save-current-buffer
|
||||
(bookmark-handle-bookmark bookmark-name)
|
||||
(buffer-string))))
|
||||
(insert str-to-insert)
|
||||
(push-mark)
|
||||
(goto-char orig-point)))
|
||||
(if (eq 'insert (get (or (bookmark-get-handler bookmark-name)
|
||||
#'bookmark-default-handler)
|
||||
'bookmark-inhibit))
|
||||
(error "Insert not supported for bookmark %s" bookmark-name)
|
||||
(let ((orig-point (point))
|
||||
(str-to-insert
|
||||
(save-current-buffer
|
||||
(bookmark-handle-bookmark bookmark-name)
|
||||
(buffer-string))))
|
||||
(insert str-to-insert)
|
||||
(push-mark)
|
||||
(goto-char orig-point))))
|
||||
|
||||
|
||||
;;;###autoload
|
||||
|
|
129
lisp/shell.el
129
lisp/shell.el
|
@ -700,6 +700,7 @@ command."
|
|||
(setq-local paragraph-separate "\\'")
|
||||
(setq-local paragraph-start comint-prompt-regexp)
|
||||
(setq-local font-lock-defaults '(shell-font-lock-keywords t))
|
||||
(setq-local bookmark-make-record-function #'shell-bookmark-make-record)
|
||||
(setq-local shell-dirstack nil)
|
||||
(setq-local shell-last-dir nil)
|
||||
(setq-local comint-get-old-input #'shell-get-old-input)
|
||||
|
@ -1862,6 +1863,134 @@ to make `shell-highlight-undef-mode' redo its setup."
|
|||
(when shell-highlight-undef-mode
|
||||
(shell-highlight-undef-mode 1)))
|
||||
|
||||
;;; Bookmark support:
|
||||
|
||||
(declare-function bookmark-prop-get "bookmark" (bookmark prop))
|
||||
|
||||
(defcustom shell-bookmark-name-function #'shell-bookmark-name-from-default-directory
|
||||
"Function to generate a shell bookmark name.
|
||||
The default is `shell-bookmark-name', which see."
|
||||
:group 'shell
|
||||
:type `(choice (function-item ,#'shell-bookmark-name-from-default-directory)
|
||||
(function-item ,#'shell-bookmark-name-from-buffer-name)
|
||||
function)
|
||||
:version "31.1")
|
||||
|
||||
(defun shell-bookmark-name-from-default-directory ()
|
||||
"Return a `shell-mode' bookmark name based on `default-directory'.
|
||||
Return \"shell-\" appended with the final path component of the buffer's
|
||||
`default-directory'."
|
||||
(format "shell-%s"
|
||||
(file-name-nondirectory
|
||||
(directory-file-name
|
||||
(file-name-directory default-directory)))))
|
||||
|
||||
(defun shell-bookmark-name-from-buffer-name ()
|
||||
"Return a `shell-mode' bookmark name based on buffer name'.
|
||||
Return `buffer-name' stripped of its count suffix; e.g., \"*shell*<2>\",
|
||||
if adorned by `rename-uniquely', which see."
|
||||
(replace-regexp-in-string "<[[:digit:]]+>\\'" "" (buffer-name)))
|
||||
|
||||
(defvar shell-bookmark-defaults-function #'shell-bookmark-defaults
|
||||
"Function to generate a list of default shell bookmark names.
|
||||
This list is used by `bookmark-set' and prompted by
|
||||
`read-from-minibuffer'.")
|
||||
|
||||
(defun shell-bookmark-defaults ()
|
||||
"Return bookmark name options for the current `shell-mode' buffer."
|
||||
(list
|
||||
(funcall shell-bookmark-name-function)
|
||||
(buffer-name)
|
||||
default-directory))
|
||||
|
||||
(defun shell-bookmark-make-record ()
|
||||
"Create a bookmark record for the current `shell-mode' buffer.
|
||||
Handle both local and remote shell buffers.
|
||||
Before creating ad-hoc multi-hop remote connections, customize either or
|
||||
both:
|
||||
`tramp-save-ad-hoc-proxies' to non-nil to persist proxy routes.
|
||||
`tramp-show-ad-hoc-proxies' to non-nil to ensure connections are fully
|
||||
qualified. This is helpful if you use the same persisted bookmarks
|
||||
file on multiple hosts."
|
||||
(let ((bookmark-shell-file-name
|
||||
(or (connection-local-value shell-file-name) sh-shell-file)))
|
||||
`((defaults . ,(funcall shell-bookmark-defaults-function))
|
||||
(location . ,default-directory)
|
||||
(shell-file-name . ,bookmark-shell-file-name)
|
||||
(handler . shell-bookmark-jump))))
|
||||
|
||||
(defvar shell-bookmark-jump-non-essential nil
|
||||
"If non-nil, new remote connections are inhibited in shell-bookmark-jump.
|
||||
This is useful when loading a session via `desktop-read' or another
|
||||
session-management package.")
|
||||
|
||||
;;;###autoload
|
||||
(defun shell-bookmark-jump (bookmark)
|
||||
"Default BOOKMARK handler for shell buffers.
|
||||
Create a shell buffer with its `default-directory', shell process, and
|
||||
buffer name from the bookmark. If there is an existing shell buffer of
|
||||
the same name, default `shell-mode' behavior is to reuse that buffer.
|
||||
|
||||
For a remote shell `default-directory' will be the remote file name.
|
||||
Remote shell buffers reuse existing connections that match the remote
|
||||
file name, or may prompt you to create a new connection. Bind
|
||||
`tramp-show-ad-hoc-proxies' to non-nil to ensure multi-hop remote
|
||||
connections are fully qualified.
|
||||
|
||||
If called with a single \\[universal-argument] prefix, a new shell
|
||||
buffer will be created if there is an existing buffer with the same
|
||||
name. The new buffer name is made unique using `rename-uniquely', which
|
||||
see.
|
||||
|
||||
If called with a double \\[universal-argument] prefix, new remote
|
||||
connections are inhibited, though an existing connection will be reused.
|
||||
You can make a remote connection manually by reloading the buffer using
|
||||
\\[find-alternate-file] or create a new shell using \\[shell].
|
||||
|
||||
If called with a triple \\[universal-argument] prefix, a new buffer will
|
||||
be created if necessary, and new remote connections are inhibited."
|
||||
(let* ((bookmark-default-directory (bookmark-prop-get bookmark 'location))
|
||||
(default-directory bookmark-default-directory)
|
||||
(explicit-shell-file-name (bookmark-prop-get bookmark 'shell-file-name))
|
||||
(prefix-arg (prefix-numeric-value current-prefix-arg))
|
||||
(maybe-new-shell (or (= 4 prefix-arg) (= 64 prefix-arg)))
|
||||
(non-essential (or shell-bookmark-jump-non-essential
|
||||
(= 16 prefix-arg) (= 64 prefix-arg)))
|
||||
(shell-buffer-name (car bookmark))
|
||||
(shell-buffer-name (if (and maybe-new-shell
|
||||
(comint-check-proc shell-buffer-name))
|
||||
(generate-new-buffer-name shell-buffer-name)
|
||||
shell-buffer-name)))
|
||||
;; Handle a local shell, a remote shell with an existing
|
||||
;; connection, or a remote shell needing a connection and new
|
||||
;; connections not inhibited.
|
||||
(if (or (not (file-remote-p default-directory))
|
||||
(file-remote-p default-directory nil 'connected)
|
||||
(and (not non-essential)
|
||||
(not (file-remote-p default-directory nil 'connected))))
|
||||
(shell shell-buffer-name)
|
||||
;; Handle a remote shell with no matching active connection and if
|
||||
;; new connections are inhibited.
|
||||
(let* ((file-name-handler-alist nil)
|
||||
;; Ignore file-name-handler-alist to guard
|
||||
;; abbreviate-file-name, et.al., which are remote aware.
|
||||
;; The macro without-remote-files is insufficient for this
|
||||
;; case.
|
||||
(shell-buffer
|
||||
(shell shell-buffer-name)))
|
||||
(with-current-buffer shell-buffer
|
||||
;; Allow reloading or M-x shell to attempt a remote connection.
|
||||
(setq default-directory bookmark-default-directory)
|
||||
(setq list-buffers-directory bookmark-default-directory)
|
||||
;; Inhibit features that may cause remote connection attempts.
|
||||
;; These settings revert when the user reloads the buffer.
|
||||
(dirtrack-mode -1)
|
||||
(shell-dirtrack-mode -1)
|
||||
(delq (assoc "7" ansi-osc-handlers) ; ansi-osc-directory-tracker
|
||||
ansi-osc-handlers))))))
|
||||
(put #'shell-bookmark-jump 'bookmark-handler-type "Shell")
|
||||
(put #'shell-bookmark-jump 'bookmark-inhibit 'insert)
|
||||
|
||||
(provide 'shell)
|
||||
|
||||
;;; shell.el ends here
|
||||
|
|
Loading…
Add table
Reference in a new issue