aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorshipmints2025-03-06 12:04:57 +0100
committerMichael Albinus2025-03-06 12:04:57 +0100
commitc1ddf612d592fcaa3fae66b24933e2aab78406f1 (patch)
tree8f059be012660cb25a1a9942d51fe48f8f8edeac
parentbf027eb6ff267162c116006703698668794d37cc (diff)
downloademacs-c1ddf612d592fcaa3fae66b24933e2aab78406f1.tar.gz
emacs-c1ddf612d592fcaa3fae66b24933e2aab78406f1.zip
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.
-rw-r--r--doc/emacs/misc.texi48
-rw-r--r--etc/NEWS10
-rw-r--r--lisp/bookmark.el20
-rw-r--r--lisp/shell.el129
4 files changed, 198 insertions, 9 deletions
diff --git a/doc/emacs/misc.texi b/doc/emacs/misc.texi
index 22af77b62c6..6ffa185cd49 100644
--- a/doc/emacs/misc.texi
+++ b/doc/emacs/misc.texi
@@ -773,6 +773,7 @@ See the Eshell Info manual, which is distributed with Emacs.
773* Shell Prompts:: Two ways to recognize shell prompts. 773* Shell Prompts:: Two ways to recognize shell prompts.
774* History: Shell History. Repeating previous commands in a shell buffer. 774* History: Shell History. Repeating previous commands in a shell buffer.
775* Directory Tracking:: Keeping track when the subshell changes directory. 775* Directory Tracking:: Keeping track when the subshell changes directory.
776* Shell Bookmarks:: Save and restore local and remote shell buffers.
776* Options: Shell Options. Options for customizing Shell mode. 777* Options: Shell Options. Options for customizing Shell mode.
777* Terminal emulator:: An Emacs window as a terminal emulator. 778* Terminal emulator:: An Emacs window as a terminal emulator.
778* Term Mode:: Special Emacs commands used in Term mode. 779* Term Mode:: Special Emacs commands used in Term mode.
@@ -1199,7 +1200,7 @@ subshell:
1199 1200
1200By default, Shell mode handles common @acronym{ANSI} escape codes (for 1201By default, Shell mode handles common @acronym{ANSI} escape codes (for
1201instance, for changing the color of text). Emacs also optionally 1202instance, for changing the color of text). Emacs also optionally
1202supports some extend escape codes, like some of the @acronym{OSC} 1203supports some extended escape codes, like some of the @acronym{OSC}
1203(Operating System Codes) if you put the following in your init file: 1204(Operating System Codes) if you put the following in your init file:
1204 1205
1205@lisp 1206@lisp
@@ -1503,6 +1504,51 @@ working directory; see the documentation of the variable
1503dirtrack-mode} in the Shell buffer, or add @code{dirtrack-mode} to 1504dirtrack-mode} in the Shell buffer, or add @code{dirtrack-mode} to
1504@code{shell-mode-hook} (@pxref{Hooks}). 1505@code{shell-mode-hook} (@pxref{Hooks}).
1505 1506
1507@node Shell Bookmarks
1508@subsection Shell Bookmarks
1509@cindex shell bookmarks
1510
1511Shell mode buffers can be bookmarked, and both local and remote
1512(@pxref{Remote Files}) shell buffers are supported. @xref{Bookmarks}.
1513
1514 Opening, or ``jumping'' to, a bookmarked shell restores its buffer
1515name, its current directory, and will create a remote connection, as
1516necessary, using the shell command you used to create the remote buffer.
1517
1518@vindex shell-bookmark-name-function
1519@findex shell-bookmark-name-from-default-directory
1520@findex shell-bookmark-name-from-buffer-name
1521 The option @code{shell-bookmark-name-function} can be customized to
1522suit your preferences. It defaults to the function
1523@code{shell-bookmark-name-from-default-directory} which uses the final
1524component of the buffer's @code{default-directory}. An alternate
1525function, @code{shell-bookmark-name-from-buffer-name}, uses the buffer's
1526name with its @code{rename-uniquely} suffix brackets "<>" stripped. You
1527can bind this option to your own function.
1528
1529 You can inhibit remote connections when you open a remote shell
1530bookmark. This is useful when you restore sessions with
1531@code{desktop-load}, or via another session-management package, to avoid
1532time delays establishing connections. You can establish a connection on
1533an unconnected remote buffer using the command @kbd{C-x C-v}
1534(@code{find-alternate-file}). To inhibit a connection interactively,
1535give a prefix argument before invoking the open/jump bookmark menu item,
1536or before invoking the command @code{bookmark-jump}. @footnote{To
1537inhibit a connection programmatically, refer to the documentation for
1538the variable @code{shell-bookmark-jump-non-essential}.} @footnote{To
1539properly handle multi-hop remote connections, refer to the documentation
1540for the function @code{shell-bookmark-jump}.}
1541
1542Note: Before creating ad-hoc multi-hop remote connections, customize
1543either or both:
1544@code{tramp-save-ad-hoc-proxies} to non-@code{nil} to persist proxy
1545routes.
1546@code{tramp-show-ad-hoc-proxies} to non-@code{nil} to ensure connections
1547are fully qualified. This is helpful if you use the same persisted
1548bookmarks file on multiple hosts.
1549
1550@xref{Top, The Tramp Manual,, tramp, The Tramp Manual}.
1551
1506@node Shell Options 1552@node Shell Options
1507@subsection Shell Mode Options 1553@subsection Shell Mode Options
1508 1554
diff --git a/etc/NEWS b/etc/NEWS
index 78a241abfd3..864bfbf595b 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -690,6 +690,16 @@ It removes all the buttons in the specified region.
690 690
691** Shell 691** Shell
692 692
693---
694*** Shell buffers now support bookmarks.
695
696You can now bookmark local and remote shell buffers using the bookmark
697menu 'bookmark-bmenu-list', or by using the command 'bookmark-set'.
698Shell bookmarks can be loaded via the menu and by using the command
699'bookmark-jump', which open a bookmarked shell, restore its buffer name,
700its current directory, and create a remote connection, if necessary.
701You can customize 'shell-bookmark-name-function'.
702
693*** New command to complete the shell history. 703*** New command to complete the shell history.
694'comint-complete-input-ring' ('C-x <up>') is like 'minibuffer-complete-history' 704'comint-complete-input-ring' ('C-x <up>') is like 'minibuffer-complete-history'
695but completes on comint inputs. 705but completes on comint inputs.
diff --git a/lisp/bookmark.el b/lisp/bookmark.el
index 8495f33cb5f..54de574ab76 100644
--- a/lisp/bookmark.el
+++ b/lisp/bookmark.el
@@ -1530,14 +1530,18 @@ this."
1530 (interactive (list (bookmark-completing-read "Insert bookmark contents"))) 1530 (interactive (list (bookmark-completing-read "Insert bookmark contents")))
1531 (bookmark-maybe-historicize-string bookmark-name) 1531 (bookmark-maybe-historicize-string bookmark-name)
1532 (bookmark-maybe-load-default-file) 1532 (bookmark-maybe-load-default-file)
1533 (let ((orig-point (point)) 1533 (if (eq 'insert (get (or (bookmark-get-handler bookmark-name)
1534 (str-to-insert 1534 #'bookmark-default-handler)
1535 (save-current-buffer 1535 'bookmark-inhibit))
1536 (bookmark-handle-bookmark bookmark-name) 1536 (error "Insert not supported for bookmark %s" bookmark-name)
1537 (buffer-string)))) 1537 (let ((orig-point (point))
1538 (insert str-to-insert) 1538 (str-to-insert
1539 (push-mark) 1539 (save-current-buffer
1540 (goto-char orig-point))) 1540 (bookmark-handle-bookmark bookmark-name)
1541 (buffer-string))))
1542 (insert str-to-insert)
1543 (push-mark)
1544 (goto-char orig-point))))
1541 1545
1542 1546
1543;;;###autoload 1547;;;###autoload
diff --git a/lisp/shell.el b/lisp/shell.el
index a3834634df7..25109a50b43 100644
--- a/lisp/shell.el
+++ b/lisp/shell.el
@@ -700,6 +700,7 @@ command."
700 (setq-local paragraph-separate "\\'") 700 (setq-local paragraph-separate "\\'")
701 (setq-local paragraph-start comint-prompt-regexp) 701 (setq-local paragraph-start comint-prompt-regexp)
702 (setq-local font-lock-defaults '(shell-font-lock-keywords t)) 702 (setq-local font-lock-defaults '(shell-font-lock-keywords t))
703 (setq-local bookmark-make-record-function #'shell-bookmark-make-record)
703 (setq-local shell-dirstack nil) 704 (setq-local shell-dirstack nil)
704 (setq-local shell-last-dir nil) 705 (setq-local shell-last-dir nil)
705 (setq-local comint-get-old-input #'shell-get-old-input) 706 (setq-local comint-get-old-input #'shell-get-old-input)
@@ -1862,6 +1863,134 @@ to make `shell-highlight-undef-mode' redo its setup."
1862 (when shell-highlight-undef-mode 1863 (when shell-highlight-undef-mode
1863 (shell-highlight-undef-mode 1))) 1864 (shell-highlight-undef-mode 1)))
1864 1865
1866;;; Bookmark support:
1867
1868(declare-function bookmark-prop-get "bookmark" (bookmark prop))
1869
1870(defcustom shell-bookmark-name-function #'shell-bookmark-name-from-default-directory
1871 "Function to generate a shell bookmark name.
1872The default is `shell-bookmark-name', which see."
1873 :group 'shell
1874 :type `(choice (function-item ,#'shell-bookmark-name-from-default-directory)
1875 (function-item ,#'shell-bookmark-name-from-buffer-name)
1876 function)
1877 :version "31.1")
1878
1879(defun shell-bookmark-name-from-default-directory ()
1880 "Return a `shell-mode' bookmark name based on `default-directory'.
1881Return \"shell-\" appended with the final path component of the buffer's
1882`default-directory'."
1883 (format "shell-%s"
1884 (file-name-nondirectory
1885 (directory-file-name
1886 (file-name-directory default-directory)))))
1887
1888(defun shell-bookmark-name-from-buffer-name ()
1889 "Return a `shell-mode' bookmark name based on buffer name'.
1890Return `buffer-name' stripped of its count suffix; e.g., \"*shell*<2>\",
1891if adorned by `rename-uniquely', which see."
1892 (replace-regexp-in-string "<[[:digit:]]+>\\'" "" (buffer-name)))
1893
1894(defvar shell-bookmark-defaults-function #'shell-bookmark-defaults
1895 "Function to generate a list of default shell bookmark names.
1896This list is used by `bookmark-set' and prompted by
1897`read-from-minibuffer'.")
1898
1899(defun shell-bookmark-defaults ()
1900 "Return bookmark name options for the current `shell-mode' buffer."
1901 (list
1902 (funcall shell-bookmark-name-function)
1903 (buffer-name)
1904 default-directory))
1905
1906(defun shell-bookmark-make-record ()
1907 "Create a bookmark record for the current `shell-mode' buffer.
1908Handle both local and remote shell buffers.
1909Before creating ad-hoc multi-hop remote connections, customize either or
1910both:
1911`tramp-save-ad-hoc-proxies' to non-nil to persist proxy routes.
1912`tramp-show-ad-hoc-proxies' to non-nil to ensure connections are fully
1913 qualified. This is helpful if you use the same persisted bookmarks
1914 file on multiple hosts."
1915 (let ((bookmark-shell-file-name
1916 (or (connection-local-value shell-file-name) sh-shell-file)))
1917 `((defaults . ,(funcall shell-bookmark-defaults-function))
1918 (location . ,default-directory)
1919 (shell-file-name . ,bookmark-shell-file-name)
1920 (handler . shell-bookmark-jump))))
1921
1922(defvar shell-bookmark-jump-non-essential nil
1923 "If non-nil, new remote connections are inhibited in shell-bookmark-jump.
1924This is useful when loading a session via `desktop-read' or another
1925session-management package.")
1926
1927;;;###autoload
1928(defun shell-bookmark-jump (bookmark)
1929 "Default BOOKMARK handler for shell buffers.
1930Create a shell buffer with its `default-directory', shell process, and
1931buffer name from the bookmark. If there is an existing shell buffer of
1932the same name, default `shell-mode' behavior is to reuse that buffer.
1933
1934For a remote shell `default-directory' will be the remote file name.
1935Remote shell buffers reuse existing connections that match the remote
1936file name, or may prompt you to create a new connection. Bind
1937`tramp-show-ad-hoc-proxies' to non-nil to ensure multi-hop remote
1938connections are fully qualified.
1939
1940If called with a single \\[universal-argument] prefix, a new shell
1941buffer will be created if there is an existing buffer with the same
1942name. The new buffer name is made unique using `rename-uniquely', which
1943see.
1944
1945If called with a double \\[universal-argument] prefix, new remote
1946connections are inhibited, though an existing connection will be reused.
1947You can make a remote connection manually by reloading the buffer using
1948\\[find-alternate-file] or create a new shell using \\[shell].
1949
1950If called with a triple \\[universal-argument] prefix, a new buffer will
1951be created if necessary, and new remote connections are inhibited."
1952 (let* ((bookmark-default-directory (bookmark-prop-get bookmark 'location))
1953 (default-directory bookmark-default-directory)
1954 (explicit-shell-file-name (bookmark-prop-get bookmark 'shell-file-name))
1955 (prefix-arg (prefix-numeric-value current-prefix-arg))
1956 (maybe-new-shell (or (= 4 prefix-arg) (= 64 prefix-arg)))
1957 (non-essential (or shell-bookmark-jump-non-essential
1958 (= 16 prefix-arg) (= 64 prefix-arg)))
1959 (shell-buffer-name (car bookmark))
1960 (shell-buffer-name (if (and maybe-new-shell
1961 (comint-check-proc shell-buffer-name))
1962 (generate-new-buffer-name shell-buffer-name)
1963 shell-buffer-name)))
1964 ;; Handle a local shell, a remote shell with an existing
1965 ;; connection, or a remote shell needing a connection and new
1966 ;; connections not inhibited.
1967 (if (or (not (file-remote-p default-directory))
1968 (file-remote-p default-directory nil 'connected)
1969 (and (not non-essential)
1970 (not (file-remote-p default-directory nil 'connected))))
1971 (shell shell-buffer-name)
1972 ;; Handle a remote shell with no matching active connection and if
1973 ;; new connections are inhibited.
1974 (let* ((file-name-handler-alist nil)
1975 ;; Ignore file-name-handler-alist to guard
1976 ;; abbreviate-file-name, et.al., which are remote aware.
1977 ;; The macro without-remote-files is insufficient for this
1978 ;; case.
1979 (shell-buffer
1980 (shell shell-buffer-name)))
1981 (with-current-buffer shell-buffer
1982 ;; Allow reloading or M-x shell to attempt a remote connection.
1983 (setq default-directory bookmark-default-directory)
1984 (setq list-buffers-directory bookmark-default-directory)
1985 ;; Inhibit features that may cause remote connection attempts.
1986 ;; These settings revert when the user reloads the buffer.
1987 (dirtrack-mode -1)
1988 (shell-dirtrack-mode -1)
1989 (delq (assoc "7" ansi-osc-handlers) ; ansi-osc-directory-tracker
1990 ansi-osc-handlers))))))
1991(put #'shell-bookmark-jump 'bookmark-handler-type "Shell")
1992(put #'shell-bookmark-jump 'bookmark-inhibit 'insert)
1993
1865(provide 'shell) 1994(provide 'shell)
1866 1995
1867;;; shell.el ends here 1996;;; shell.el ends here