diff options
| author | shipmints | 2025-03-06 12:04:57 +0100 |
|---|---|---|
| committer | Michael Albinus | 2025-03-06 12:04:57 +0100 |
| commit | c1ddf612d592fcaa3fae66b24933e2aab78406f1 (patch) | |
| tree | 8f059be012660cb25a1a9942d51fe48f8f8edeac | |
| parent | bf027eb6ff267162c116006703698668794d37cc (diff) | |
| download | emacs-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.texi | 48 | ||||
| -rw-r--r-- | etc/NEWS | 10 | ||||
| -rw-r--r-- | lisp/bookmark.el | 20 | ||||
| -rw-r--r-- | lisp/shell.el | 129 |
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 | ||
| 1200 | By default, Shell mode handles common @acronym{ANSI} escape codes (for | 1201 | By default, Shell mode handles common @acronym{ANSI} escape codes (for |
| 1201 | instance, for changing the color of text). Emacs also optionally | 1202 | instance, for changing the color of text). Emacs also optionally |
| 1202 | supports some extend escape codes, like some of the @acronym{OSC} | 1203 | supports 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 | |||
| 1503 | dirtrack-mode} in the Shell buffer, or add @code{dirtrack-mode} to | 1504 | dirtrack-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 | |||
| 1511 | Shell 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 | ||
| 1515 | name, its current directory, and will create a remote connection, as | ||
| 1516 | necessary, 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 | ||
| 1522 | suit your preferences. It defaults to the function | ||
| 1523 | @code{shell-bookmark-name-from-default-directory} which uses the final | ||
| 1524 | component of the buffer's @code{default-directory}. An alternate | ||
| 1525 | function, @code{shell-bookmark-name-from-buffer-name}, uses the buffer's | ||
| 1526 | name with its @code{rename-uniquely} suffix brackets "<>" stripped. You | ||
| 1527 | can bind this option to your own function. | ||
| 1528 | |||
| 1529 | You can inhibit remote connections when you open a remote shell | ||
| 1530 | bookmark. This is useful when you restore sessions with | ||
| 1531 | @code{desktop-load}, or via another session-management package, to avoid | ||
| 1532 | time delays establishing connections. You can establish a connection on | ||
| 1533 | an unconnected remote buffer using the command @kbd{C-x C-v} | ||
| 1534 | (@code{find-alternate-file}). To inhibit a connection interactively, | ||
| 1535 | give a prefix argument before invoking the open/jump bookmark menu item, | ||
| 1536 | or before invoking the command @code{bookmark-jump}. @footnote{To | ||
| 1537 | inhibit a connection programmatically, refer to the documentation for | ||
| 1538 | the variable @code{shell-bookmark-jump-non-essential}.} @footnote{To | ||
| 1539 | properly handle multi-hop remote connections, refer to the documentation | ||
| 1540 | for the function @code{shell-bookmark-jump}.} | ||
| 1541 | |||
| 1542 | Note: Before creating ad-hoc multi-hop remote connections, customize | ||
| 1543 | either or both: | ||
| 1544 | @code{tramp-save-ad-hoc-proxies} to non-@code{nil} to persist proxy | ||
| 1545 | routes. | ||
| 1546 | @code{tramp-show-ad-hoc-proxies} to non-@code{nil} to ensure connections | ||
| 1547 | are fully qualified. This is helpful if you use the same persisted | ||
| 1548 | bookmarks 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 | ||
| @@ -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 | |||
| 696 | You can now bookmark local and remote shell buffers using the bookmark | ||
| 697 | menu 'bookmark-bmenu-list', or by using the command 'bookmark-set'. | ||
| 698 | Shell bookmarks can be loaded via the menu and by using the command | ||
| 699 | 'bookmark-jump', which open a bookmarked shell, restore its buffer name, | ||
| 700 | its current directory, and create a remote connection, if necessary. | ||
| 701 | You 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' |
| 695 | but completes on comint inputs. | 705 | but 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. | ||
| 1872 | The 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'. | ||
| 1881 | Return \"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'. | ||
| 1890 | Return `buffer-name' stripped of its count suffix; e.g., \"*shell*<2>\", | ||
| 1891 | if 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. | ||
| 1896 | This 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. | ||
| 1908 | Handle both local and remote shell buffers. | ||
| 1909 | Before creating ad-hoc multi-hop remote connections, customize either or | ||
| 1910 | both: | ||
| 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. | ||
| 1924 | This is useful when loading a session via `desktop-read' or another | ||
| 1925 | session-management package.") | ||
| 1926 | |||
| 1927 | ;;;###autoload | ||
| 1928 | (defun shell-bookmark-jump (bookmark) | ||
| 1929 | "Default BOOKMARK handler for shell buffers. | ||
| 1930 | Create a shell buffer with its `default-directory', shell process, and | ||
| 1931 | buffer name from the bookmark. If there is an existing shell buffer of | ||
| 1932 | the same name, default `shell-mode' behavior is to reuse that buffer. | ||
| 1933 | |||
| 1934 | For a remote shell `default-directory' will be the remote file name. | ||
| 1935 | Remote shell buffers reuse existing connections that match the remote | ||
| 1936 | file 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 | ||
| 1938 | connections are fully qualified. | ||
| 1939 | |||
| 1940 | If called with a single \\[universal-argument] prefix, a new shell | ||
| 1941 | buffer will be created if there is an existing buffer with the same | ||
| 1942 | name. The new buffer name is made unique using `rename-uniquely', which | ||
| 1943 | see. | ||
| 1944 | |||
| 1945 | If called with a double \\[universal-argument] prefix, new remote | ||
| 1946 | connections are inhibited, though an existing connection will be reused. | ||
| 1947 | You can make a remote connection manually by reloading the buffer using | ||
| 1948 | \\[find-alternate-file] or create a new shell using \\[shell]. | ||
| 1949 | |||
| 1950 | If called with a triple \\[universal-argument] prefix, a new buffer will | ||
| 1951 | be 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 |