[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
master 0359317: Add renaming of remote buffer file names to Tramp
From: |
Michael Albinus |
Subject: |
master 0359317: Add renaming of remote buffer file names to Tramp |
Date: |
Wed, 20 Nov 2019 07:45:41 -0500 (EST) |
branch: master
commit 035931777bd89b939436fd1d8a2b8d5a80ede095
Author: Michael Albinus <address@hidden>
Commit: Michael Albinus <address@hidden>
Add renaming of remote buffer file names to Tramp
* doc/misc/tramp.texi (Default User): Fix typo.
(Cleanup remote connections): Adapt arguments of
`tramp-cleanup-connection'.
(Renaming remote files): New node.
(Frequently Asked Questions): New item "How to save files when a
remote host isn't reachable anymore?".
* etc/NEWS: Add `tramp-rename-files' and `tramp-rename-these-files'.
* lisp/net/tramp-cmds.el (tramp-default-rename-alist)
(tramp-confirm-rename-file-names): New defcustoms.
(tramp-rename-read-file-name-dir)
(tramp-rename-read-file-name-init): New defsubsts.
(tramp-default-rename-file, tramp-rename-files)
(tramp-rename-these-files): New defuns.
* lisp/net/tramp-integration.el (ido, ivy): Integrate with them.
---
doc/misc/tramp.texi | 140 ++++++++++++++++++++--
etc/NEWS | 5 +
lisp/net/tramp-cmds.el | 267 +++++++++++++++++++++++++++++++++++++++++-
lisp/net/tramp-integration.el | 16 +++
4 files changed, 416 insertions(+), 12 deletions(-)
diff --git a/doc/misc/tramp.texi b/doc/misc/tramp.texi
index 67472a8..dd1378a 100644
--- a/doc/misc/tramp.texi
+++ b/doc/misc/tramp.texi
@@ -153,6 +153,7 @@ Using @value{tramp}
* Ad-hoc multi-hops:: Declaring multiple hops in the file name.
* Remote processes:: Integration with other Emacs packages.
* Cleanup remote connections:: Cleanup remote connections.
+* Renaming remote files:: Renaming remote files.
* Archive file names:: Access to files in file archives.
How file names, directories and localnames are mangled and managed
@@ -1409,7 +1410,7 @@ use the @samp{john} as the default user for the domain
A Caution: @value{tramp} will override any default user specified in
the configuration files outside Emacs, such as @file{~/.ssh/config}.
To stop @value{tramp} from applying the default value, set the
-corresponding alist entry to nil:
+corresponding alist entry to @code{nil}:
@lisp
@group
@@ -2710,6 +2711,7 @@ is a feature of Emacs that may cause missed prompts when
using
* Ad-hoc multi-hops:: Declaring multiple hops in the file name.
* Remote processes:: Integration with other Emacs packages.
* Cleanup remote connections:: Cleanup remote connections.
+* Renaming remote files:: Renaming remote files.
* Archive file names:: Access to files in file archives.
@end menu
@@ -3371,9 +3373,9 @@ To open @command{powershell} as a remote shell, use this:
@value{tramp} provides several ways to flush remote connections.
-@deffn Command tramp-cleanup-connection vec
-This command flushes all connection related objects. @option{vec} is
-the internal representation of a remote connection. When called
+@deffn Command tramp-cleanup-connection vec &optional keep-debug keep-password
+This command flushes all connection related objects. @var{vec} is the
+internal representation of a remote connection. When called
interactively, this command lists active remote connections in the
minibuffer. Each connection is of the format
@file{@trampfn{method,user@@host,}}.
@@ -3383,11 +3385,14 @@ Flushing remote connections also cleans the password
cache
(@pxref{Connection caching}), and recentf cache (@pxref{File
Conveniences, , , emacs}). It also deletes session timers
(@pxref{Predefined connection information}) and connection buffers.
+
+If @var{keep-debug} is non-@code{nil}, the debug buffer is kept. A
+non-@code{nil} @var{keep-password} preserves the password cache.
@end deffn
@deffn Command tramp-cleanup-this-connection
-Flushes only the current buffer's remote connection objects, the same
-as in @code{tramp-cleanup-connection}.
+Flushes the current buffer's remote connection objects, the same as in
+@code{tramp-cleanup-connection}.
@end deffn
@deffn Command tramp-cleanup-all-connections
@@ -3404,6 +3409,112 @@ killing all buffers related to remote connections.
@end deffn
+@node Renaming remote files
+@section Renaming remote files
+@cindex save remote files
+
+Sometimes, it is desirable to safe file contents of buffers visiting a
+given remote host. This could happen for example, if the local host
+changes its network integration, and the remote host is not reachable
+anymore.
+
+@deffn Command tramp-rename-files source target
+Replace in all buffers the visiting file name from @var{source} to
+@var{target}. @var{source} is a remote directory name, which could
+contain also a localname part. @var{target} is the directory name
+@var{source} is replaced with. Often, @var{target} is a remote
+directory name on another host, but it can also be a local directory
+name. If @var{target} has no local part, the local part from
+@var{source} is used.
+
+If @var{target} is @code{nil}, it is selected according to the first
+match in @code{tramp-default-rename-alist}. If called interactively,
+this match is offered as initial value for selection.
+
+On all buffers, which have a @code{buffer-file-name} matching
+@var{source}, this name is modified by replacing @var{source} with
+@var{target}. This is applied by calling
+@code{set-visited-file-name}. The new @code{buffer-file-name} is
+prompted for modification in the minibuffer. The buffers are marked
+modified, and must be saved explicitly.
+
+If user option @code{tramp-confirm-rename-file-names} is nil, changing
+the file name happens without confirmation. This requires a
+matching entry in @code{tramp-default-rename-alist}.
+
+Remote buffers related to the remote connection identified by
+@var{source}, which are not visiting files, or which are visiting
+files not matching @var{source}, are not modified.
+
+Interactively, @var{target} is selected from
+@code{tramp-default-rename-alist} without confirmation if the prefix
+argument is non-@code{nil}.
+
+The remote connection identified by @var{source} is flushed by
+@code{tramp-cleanup-connection}.
+@end deffn
+
+@deffn Command tramp-rename-these-files target
+Replace visiting file names to @var{target}. The current buffer must
+be related to a remote connection. In all buffers, which are visiting
+a file with the same directory name, the buffer file name is changed.
+
+Interactively, @var{target} is selected from
+@code{tramp-default-rename-alist} without confirmation if the prefix
+argument is non-@code{nil}.
+@end deffn
+
+@defopt tramp-default-rename-alist
+The default target for renaming remote buffer file names. This is an
+alist of cons cells @code{(source . target)}. The first matching item
+specifies the target to be applied for renaming buffer file names from
+source via @code{tramp-rename-files}. @code{source} is a regular
+expressions, which matches a remote file name. @code{target} must be
+a directory name, which could be remote (including remote directories
+Tramp infers by default, such as @samp{@trampfn{method,user@@host,}}).
+
+@code{target} can contain the patterns @code{%m}, @code{%u} or
+@code{%h}, which are replaced by the method name, user name or host
+name of @code{source} when calling @code{tramp-rename-files}.
+
+@code{source} could also be a Lisp form, which will be evaluated. The
+result must be a string or nil, which is interpreted as a regular
+expression which always matches.
+
+Example entries:
+
+@lisp
+@group
+("@trampfn{ssh,badhost,/path/to/dir/}"
+ . "@trampfn{ssh,goodhost,/path/to/another/dir/}")
+@end group
+@end lisp
+
+would trigger renaming of buffer file names on @samp{badhost} to
+@samp{goodhost}, including changing the directory name.
+
+@lisp
+("@trampfn{ssh,.+\\\\.company\\\\.org,}" .
"@value{prefix}ssh@value{postfixhop}multi.hop|ssh@value{postfixhop}%h@value{postfix}")
+@end lisp
+
+routes all connections to a host in @samp{company.org} via
+@samp{@trampfn{ssh,multi.hop,}}, which might be useful when using
+Emacs outside the company network.
+
+@lisp
+(nil . "~/saved-files/%m:%u@@%h/")
+@end lisp
+
+saves all remote files locally, with a directory name including method
+name, user name and host name of the remote connection.
+@end defopt
+
+@defopt tramp-confirm-rename-file-names
+Whether renaming a buffer file name by @code{tramp-rename-files} or
+@code{tramp-rename-these-files} must be confirmed.
+@end defopt
+
+
@node Archive file names
@section Archive file names
@cindex file archives
@@ -3412,7 +3523,7 @@ killing all buffers related to remote connections.
@cindex archive method
@value{tramp} offers also transparent access to files inside file
-archives. This is possible only on machines which have installed
+archives. This is possible only on hosts which have installed
@acronym{GVFS, the GNOME Virtual File System}, @ref{GVFS-based
methods}. Internally, file archives are mounted via the
@acronym{GVFS} @option{archive} method.
@@ -4440,6 +4551,21 @@ the buffer is remote. See the optional arguments of
@item
+How to save files when a remote host isn't reachable anymore?
+
+If the local machine Emacs is running on changes its network
+integration, remote hosts could become unreachable. This happens for
+example, if the local machine is moved between your office and your
+home without restarting Emacs.
+
+In such cases, the command @code{tramp-rename-files} can be used to
+alter remote buffers’ method, host, and/or directory names. This
+permits saving their contents in the same location via another network
+path, or somewhere else entirely (including locally). @pxref{Renaming
+remote files}.
+
+
+@item
How to disable other packages from calling @value{tramp}?
There are packages that call @value{tramp} without the user ever
diff --git a/etc/NEWS b/etc/NEWS
index 4887b8e..dd08675 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1837,6 +1837,11 @@ possible to configure the remote login shell. This
avoids problems
with remote hosts, where "/bin/sh" is a link to a shell which
cooperates badly with Tramp.
++++
+*** New commands 'tramp-rename-files' and 'tramp-rename-these-files'.
+They allow to save remote files somewhere else when the corresponding
+host is not reachable anymore.
+
** Rcirc
---
diff --git a/lisp/net/tramp-cmds.el b/lisp/net/tramp-cmds.el
index 56ccf73..96b11c7 100644
--- a/lisp/net/tramp-cmds.el
+++ b/lisp/net/tramp-cmds.el
@@ -195,6 +195,268 @@ This includes password cache, file cache, connection
cache, buffers."
(dolist (name (tramp-list-remote-buffers))
(when (bufferp (get-buffer name)) (kill-buffer name))))
+;;;###tramp-autoload
+(defcustom tramp-default-rename-alist nil
+ "Default target for renaming remote buffer file names.
+This is an alist of cons cells (SOURCE . TARGET). The first
+matching item specifies the target to be applied for renaming
+buffer file names from source via `tramp-rename-files'. SOURCE
+is a regular expressions, which matches a remote file name.
+TARGET must be a directory name, which could be remote (including
+remote directories Tramp infers by default, such as
+\"/method:user@host:\").
+
+TARGET can contain the patterns %m, %u or %h, which are replaced
+by the method name, user name or host name of SOURCE when calling
+`tramp-rename-files'.
+
+SOURCE could also be a Lisp form, which will be evaluated. The
+result must be a string or nil, which is interpreted as a regular
+expression which always matches."
+ :group 'tramp
+ :version "27.1"
+ :type '(repeat (cons (choice :tag "Source regexp" regexp sexp)
+ (choice :tag "Target name" string (const nil)))))
+
+;;;###tramp-autoload
+(defcustom tramp-confirm-rename-file-names t
+ "Whether renaming a buffer file name must be confirmed."
+ :group 'tramp
+ :version "27.1"
+ :type 'boolean)
+
+(defun tramp-default-rename-file (string)
+ "Determine default file name for renaming according to STRING.
+The user option `tramp-default-rename-alist' is consulted,
+finding the default mapping. If there is no matching entry, the
+function returns nil"
+ (when (tramp-tramp-file-p string)
+ (let ((tdra tramp-default-rename-alist)
+ (method (or (file-remote-p string 'method) ""))
+ (user (or (file-remote-p string 'user) ""))
+ (host (or (file-remote-p string 'host) ""))
+ item result)
+ (while (setq item (pop tdra))
+ (when (string-match-p (or (eval (car item)) "") string)
+ (setq tdra nil
+ result
+ (format-spec
+ (cdr item) (format-spec-make ?m method ?u user ?h host)))))
+ result)))
+
+(defsubst tramp-rename-read-file-name-dir (string)
+ "Return the DIR entry to be applied in `read-file-name', based on STRING."
+ (when (tramp-tramp-file-p string)
+ (substring (file-remote-p string) 0 -1)))
+
+(defsubst tramp-rename-read-file-name-init (string)
+ "Return the INIT entry to be applied in `read-file-name', based on STRING."
+ (when (tramp-tramp-file-p string)
+ (string-remove-prefix (tramp-rename-read-file-name-dir string) string)))
+
+;;;###tramp-autoload
+(defun tramp-rename-files (source target)
+ "Replace in all buffers the visiting file name from SOURCE to TARGET.
+SOURCE is a remote directory name, which could contain also a
+localname part. TARGET is the directory name SOURCE is replaced
+with. Often, TARGET is a remote directory name on another host,
+but it can also be a local directory name. If TARGET has no
+local part, the local part from SOURCE is used.
+
+If TARGET is nil, it is selected according to the first match in
+`tramp-default-rename-alist'. If called interactively, this
+match is offered as initial value for selection.
+
+On all buffers, which have a `buffer-file-name' matching SOURCE,
+this name is modified by replacing SOURCE with TARGET. This is
+applied by calling `set-visited-file-name'. The new
+`buffer-file-name' is prompted for modification in the
+minibuffer. The buffers are marked modified, and must be saved
+explicitly.
+
+If user option `tramp-confirm-rename-file-names' is nil, changing
+the file name happens without confirmation. This requires a
+matching entry in `tramp-default-rename-alist'.
+
+Remote buffers related to the remote connection identified by
+SOURCE, which are not visiting files, or which are visiting files
+not matching SOURCE, are not modified.
+
+Interactively, TARGET is selected from `tramp-default-rename-alist'
+without confirmation if the prefix argument is non-nil.
+
+The remote connection identified by SOURCE is flushed by
+`tramp-cleanup-connection'."
+ (interactive
+ (let ((connections
+ (mapcar #'tramp-make-tramp-file-name (tramp-list-connections)))
+ ;; Completion packages do their voodoo in `completing-read'
+ ;; and `read-file-name', which is often incompatible with
+ ;; Tramp. Ignore them.
+ (completing-read-function #'completing-read-default)
+ (read-file-name-function #'read-file-name-default)
+ source target)
+ (if (null connections)
+ (tramp-user-error nil "There are no remote connections.")
+ (setq source
+ ;; Likely, the source remote connection is broken. So we
+ ;; shall avoid any action on it.
+ (let (non-essential)
+ (completing-read-default
+ "Enter old Tramp connection: "
+ ;; Completion function.
+ (completion-table-dynamic
+ (lambda (string)
+ (cond
+ ;; Initially, show existing remote connections.
+ ((not (tramp-tramp-file-p string))
+ (all-completions string connections))
+ ;; There is a selected remote connection. Show
+ ;; its longest common directory path of respective
+ ;; buffers.
+ (t (mapcar
+ (lambda (buffer)
+ (let ((bfn (buffer-file-name buffer)))
+ (and (buffer-live-p buffer)
+ (tramp-equal-remote string bfn)
+ (stringp bfn) (file-name-directory bfn))))
+ (tramp-list-remote-buffers))))))
+ #'tramp-tramp-file-p t
+ ;; If the current buffer is a remote one, it is likely
+ ;; that this connection is meant. So we offer it as
+ ;; initial value. Otherwise, use the longest remote
+ ;; connection path as initial value.
+ (or (file-remote-p default-directory)
+ (try-completion "" connections))))
+
+ target
+ (when (null current-prefix-arg)
+ ;; The source remote connection shall not trigger any action.
+ ;; FIXME: Better error prompt when trying to access source host.
+ (let* ((default (or (tramp-default-rename-file source) source))
+ (dir (tramp-rename-read-file-name-dir default))
+ (init (tramp-rename-read-file-name-init default))
+ (tramp-ignored-file-name-regexp
+ (regexp-quote (file-remote-p source))))
+ (read-file-name-default
+ "Enter new Tramp connection: "
+ dir default 'confirm init #'file-directory-p)))))
+
+ (list source target)))
+
+ (unless (tramp-tramp-file-p source)
+ (tramp-user-error nil "Source %s must be remote." source))
+ (when (null target)
+ (or (setq target (tramp-default-rename-file source))
+ (tramp-user-error
+ nil
+ (eval-when-compile
+ (concat "There is no target specified. "
+ "Check `tramp-default-rename-alist' for a proper entry.")))))
+ (when (tramp-equal-remote source target)
+ (tramp-user-error nil "Source and target must have different remote."))
+
+ ;; Append local file name if none is specified.
+ (when (string-equal (file-remote-p target) target)
+ (setq target (concat target (file-remote-p source 'localname))))
+ ;; Make them directoy names.
+ (setq source (directory-file-name source)
+ target (directory-file-name target))
+
+ ;; Rename visited file names of source buffers.
+ (save-window-excursion
+ (save-current-buffer
+ (let ((help-form "\
+Type SPC or `y' to set visited file name,
+DEL or `n' to skip to next,
+`e' to edit the visited file name,
+ESC or `q' to quit without changing further buffers,
+`!' to change all remaining buffers with no more questions.")
+ (query-choices '(?y ?\s ?n ?\177 ?! ?e ?q ?\e))
+ (query (unless tramp-confirm-rename-file-names ?!))
+ changed-buffers)
+ (dolist (buffer (tramp-list-remote-buffers))
+ (switch-to-buffer buffer)
+ (let* ((bfn (buffer-file-name))
+ (new-bfn (and (stringp bfn)
+ (replace-regexp-in-string
+ (regexp-quote source) target bfn)))
+ (prompt (format-message
+ "Set visited file name to `%s' [Type yn!eq or %s] "
+ new-bfn (key-description (vector help-char)))))
+ (when (and (buffer-live-p buffer) (stringp bfn)
+ (string-prefix-p source bfn)
+ ;; Skip, and don't ask again.
+ (not (memq query '(?q ?\e))))
+ ;; Read prompt.
+ (unless (eq query ?!)
+ (setq query (read-char-choice prompt query-choices)))
+ ;; Edit the new buffer file name.
+ (when (eq query ?e)
+ (setq new-bfn
+ (read-file-name
+ "New visited file name: "
+ (file-name-directory new-bfn) new-bfn)))
+ ;; Set buffer file name. Remember the change.
+ (when (memq query '(?y ?\s ?! ?e))
+ (setq changed-buffers
+ (cons (list buffer bfn (buffer-modified-p))
+ changed-buffers))
+ (set-visited-file-name new-bfn))
+ ;; Quit. Revert changes if prompted by user.
+ (when (and (memq query '(?q ?\e)) changed-buffers
+ (y-or-n-p "Do you want to revert applied changes?"))
+ (dolist (item changed-buffers)
+ (with-current-buffer (car item)
+ (set-visited-file-name (nth 1 item))
+ (set-buffer-modified-p (nth 2 item)))))
+ ;; Cleanup echo area.
+ (message nil)))))))
+
+ ;; Cleanup.
+ (tramp-cleanup-connection (tramp-dissect-file-name source)))
+
+;;;###tramp-autoload
+(defun tramp-rename-these-files (target)
+ "Replace visiting file names to TARGET.
+The current buffer must be related to a remote connection. In
+all buffers, which are visiting a file with the same directory
+name, the buffer file name is changed.
+
+Interactively, TARGET is selected from `tramp-default-rename-alist'
+without confirmation if the prefix argument is non-nil.
+
+For details, see `tramp-rename-files'."
+ (interactive
+ (let ((source default-directory)
+ target
+ ;; Completion packages do their voodoo in `completing-read'
+ ;; and `read-file-name', which is often incompatible with
+ ;; Tramp. Ignore them.
+ (completing-read-function #'completing-read-default)
+ (read-file-name-function #'read-file-name-default))
+ (if (not (tramp-tramp-file-p source))
+ (tramp-user-error
+ nil
+ (substitute-command-keys
+ (concat "Current buffer is not remote. "
+ "Consider `\\[tramp-rename-files]' instead.")))
+ (setq target
+ (when (null current-prefix-arg)
+ ;; The source remote connection shall not trigger any action.
+ ;; FIXME: Better error prompt when trying to access source host.
+ (let* ((default (or (tramp-default-rename-file source) source))
+ (dir (tramp-rename-read-file-name-dir default))
+ (init (tramp-rename-read-file-name-init default))
+ (tramp-ignored-file-name-regexp
+ (regexp-quote (file-remote-p source))))
+ (read-file-name-default
+ (format "Change Tramp connection `%s': " source)
+ dir default 'confirm init #'file-directory-p)))))
+ (list target)))
+
+ (tramp-rename-files default-directory target))
+
;; Tramp version is useful in a number of situations.
;;;###tramp-autoload
@@ -424,11 +686,6 @@ please ensure that the buffers are attached to your
email.\n\n"))
;; * Clean up unused *tramp/foo* buffers after a while. (Pete Forman)
;;
-;; * WIBNI there was an interactive command prompting for Tramp
-;; method, hostname, username and filename and translates the user
-;; input into the correct filename syntax (depending on the Emacs
-;; flavor) (Reiner Steib)
-;;
;; * Let the user edit the connection properties interactively.
;; Something like `gnus-server-edit-server' in Gnus' *Server* buffer.
diff --git a/lisp/net/tramp-integration.el b/lisp/net/tramp-integration.el
index 0bb19ed..0c31076 100644
--- a/lisp/net/tramp-integration.el
+++ b/lisp/net/tramp-integration.el
@@ -36,6 +36,8 @@
(declare-function tramp-file-name-equal-p "tramp")
(declare-function tramp-tramp-file-p "tramp")
(defvar eshell-path-env)
+(defvar ido-read-file-name-non-ido)
+(defvar ivy-completing-read-handlers-alist)
(defvar recentf-exclude)
(defvar tramp-current-connection)
(defvar tramp-postfix-host-format)
@@ -170,6 +172,20 @@ NAME must be equal to `tramp-current-connection'."
(remove-hook 'tramp-cleanup-all-connections-hook
#'tramp-recentf-cleanup-all))))
+;;; Integration of ido.el:
+
+(with-eval-after-load 'ido
+ (add-to-list 'ido-read-file-name-non-ido 'tramp-rename-files)
+ (add-to-list 'ido-read-file-name-non-ido 'tramp-these-rename-files))
+
+;;; Integration of ivy.el:
+
+(with-eval-after-load 'ivy
+ (add-to-list 'ivy-completing-read-handlers-alist
+ '(tramp-rename-files . completing-read-default))
+ (add-to-list 'ivy-completing-read-handlers-alist
+ '(tramp-these-rename-files . completing-read-default)))
+
;;; Default connection-local variables for Tramp:
(defconst tramp-connection-local-default-profile
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- master 0359317: Add renaming of remote buffer file names to Tramp,
Michael Albinus <=