@end example
@end defun
+@defun file-name-completion-annotation filename
+This function returns an annotation string for @var{filename}, that is
+a string with details about @var{filename} that is formatted for
+display in the @file{*Completions*} buffer when @var{filename} appears
+as a completion candidate. These details include the file modes, size
+and last modified time of @var{filename}. If this information is not
+available, or if @var{filename} is a remote file name,
+@code{file-name-completion-annotation} returns @code{nil} instead.
+@code{read-file-name} calls this function to obtain annotation strings
+for file name completion candidates. @xref{Reading File Names}.
+@end defun
+
@node Extended Attributes
@subsection Extended File Attributes
@cindex extended file attributes
@code{file-modes}, @code{file-name-all-completions},
@code{file-name-as-directory},
@code{file-name-case-insensitive-p},
-@code{file-name-completion},
+@code{file-name-completion}, @code{file-name-completion-annotation},
@code{file-name-directory},
@code{file-name-nondirectory},
@code{file-name-sans-versions}, @code{file-newer-than-file-p},
@code{file-modes}, @code{file-name-all-completions},
@code{file-name-as-directory},
@code{file-name-case-insensitive-p},
-@code{file-name-completion},
+@code{file-name-completion}, @code{file-name-completion-annotation},
@code{file-name-directory},
@code{file-name-nondirec@discretionary{}{}{}tory},
@code{file-name-sans-versions}, @code{file-newer-than-file-p},
are aware of file name handlers, so they will return the remote UID or
GID for remote files (or -1 if the connection has no associated user).
++++
+** New function 'file-name-completion-annotation'.
+This function takes a file name and returns a string with details
+about that file, which 'read-file-name' uses as completion annotations
+for completion candidates. File name handlers can modify the behavior
+of this function.
+
+++
** 'fset', 'defalias' and 'defvaralias' now signal an error for cyclic aliases.
Previously, 'fset', 'defalias' and 'defvaralias' could be made to
(t
parent))))
+(defun file-name-attributes-completion-annotation (filename)
+ "Format file attributes of FILENAME as a completion annotation."
+ (when-let ((attrs (ignore-errors (file-attributes filename 'string))))
+ (concat (file-attribute-modes attrs)
+ " "
+ (format "%8s" (file-size-human-readable
+ (file-attribute-size attrs)))
+ " "
+ (format-time-string
+ "%Y-%m-%d %T" (file-attribute-modification-time
+ attrs))
+ " "
+ (file-attribute-user-id attrs)
+ ":"
+ (file-attribute-group-id attrs))))
+
+(defun file-name-completion-annotation (filename)
+ "Return a completion annotation for FILENAME.
+
+`read-file-name' displays the completion annotation next to
+FILENAME in the *Completions* buffer when user option
+`completions-detailed' is non-nil."
+ (if-let ((handler (find-file-name-handler
+ filename 'file-name-completion-annotation)))
+ (funcall handler 'file-name-completion-annotation filename)
+ (file-name-attributes-completion-annotation filename)))
+
(defcustom make-backup-file-name-function
#'make-backup-file-name--default-function
"A function that `make-backup-file-name' uses to create backup file names.
(let ((max-file (seq-max (mapcar #'string-width files))))
(mapcar
(lambda (file)
- (list
- file
- "" ; empty prefix
- (if-let ((attrs
- (ignore-errors
- (file-attributes
- (substitute-in-file-name
- (concat minibuffer-completion-base file))
- 'string))))
- (propertize
- (concat (propertize " " 'display
- `(space :align-to ,(+ max-file 2)))
- (file-attribute-modes attrs)
- " "
- (format "%8s" (file-size-human-readable
- (file-attribute-size attrs)))
- " "
- (format-time-string
- "%Y-%m-%d %T" (file-attribute-modification-time attrs))
- " "
- (file-attribute-user-id attrs)
- ":"
- (file-attribute-group-id attrs))
- 'face 'completions-annotations)
- "")))
+ (let ((full (substitute-in-file-name
+ (concat minibuffer-completion-base file))))
+ (list file ""
+ (if-let ((ann (file-name-completion-annotation full)))
+ (propertize
+ (concat (propertize " " 'display
+ `(space :align-to ,(+ max-file 2)))
+ ann)
+ 'face 'completions-annotations)
+ ""))))
files)))
(defun completion-file-name-table (string pred action)
(file-name-as-directory . tramp-handle-file-name-as-directory)
(file-name-case-insensitive-p . tramp-handle-file-name-case-insensitive-p)
(file-name-completion . tramp-handle-file-name-completion)
+ (file-name-completion-annotation
+ . tramp-sh-handle-file-name-completion-annotation)
(file-name-directory . tramp-handle-file-name-directory)
(file-name-nondirectory . tramp-handle-file-name-nondirectory)
;; `file-name-sans-versions' performed by default handler.
(buffer-substring (point) (line-end-position)) result)))
result))))))))))
+(defun tramp-sh-handle-file-name-completion-annotation (filename)
+ "Like `file-name-completion-annotation' for Tramp files."
+ (with-parsed-tramp-file-name filename nil
+ (when (string-match-p
+ (rx bos (or "sudo" "su" "sg" "doas" "ksu") eos) method)
+ (file-name-attributes-completion-annotation filename))))
+
;; cp, mv and ln
(defun tramp-sh-handle-add-name-to-file
(file-name-as-directory . tramp-handle-file-name-as-directory)
(file-name-case-insensitive-p . tramp-handle-file-name-case-insensitive-p)
(file-name-completion . tramp-handle-file-name-completion)
+ (file-name-completion-annotation
+ . file-name-attributes-completion-annotation)
(file-name-directory . tramp-handle-file-name-directory)
(file-name-nondirectory . tramp-handle-file-name-nondirectory)
;; `file-name-sans-versions' performed by default handler.
'(add-name-to-file copy-directory copy-file
file-equal-p file-in-directory-p
file-name-all-completions file-name-completion
+ file-name-completion-annotation
file-newer-than-file-p rename-file))
(cond
((tramp-tramp-file-p (nth 0 args)) (nth 0 args))