mu4e: improve mime-type display
Better handle some 'special' MIME-types where we need a tiny bit of processing/special casing to invent a proper file-name. Include size information when displaying the MIME-types in the actions menu.
This commit is contained in:
@ -35,7 +35,6 @@
|
|||||||
|
|
||||||
(require 'mu4e-window)
|
(require 'mu4e-window)
|
||||||
(require 'mu4e-config)
|
(require 'mu4e-config)
|
||||||
(require 'mailcap)
|
|
||||||
|
|
||||||
;;; Customization
|
;;; Customization
|
||||||
|
|
||||||
@ -72,18 +71,43 @@ Uses `mu4e-file-name-to-icon-function' if set."
|
|||||||
(when (and filename mu4e-file-name-to-icon-function)
|
(when (and filename mu4e-file-name-to-icon-function)
|
||||||
(funcall mu4e-file-name-to-icon-function filename)))
|
(funcall mu4e-file-name-to-icon-function filename)))
|
||||||
|
|
||||||
|
(defconst mu4e--mime-subtype-extension-alist
|
||||||
|
'(("plain" . "txt")
|
||||||
|
("x-shellscript" . "sh")
|
||||||
|
("svg+xml" . "svg"))
|
||||||
|
"Alist mapping MIME subtypes to file extensions for known quirks.
|
||||||
|
Used by `mu4e-mime-type-to-icon' when deriving a dummy filename
|
||||||
|
from a MIME type.
|
||||||
|
|
||||||
|
Only entries whose mapping cannot be obtained by simply stripping
|
||||||
|
a leading `x-' need to be listed here.")
|
||||||
|
|
||||||
|
(defun mu4e--mime-type-extension (mime-type)
|
||||||
|
"Derive a plausible file extension from MIME-TYPE.
|
||||||
|
|
||||||
|
Uses `mu4e--mime-subtype-extension-alist' for known quirks,
|
||||||
|
otherwise strips any `x-' prefix from the subtype (the part
|
||||||
|
after the `/').
|
||||||
|
|
||||||
|
Returns nil for malformed MIME types."
|
||||||
|
(when (and mime-type (string-match "/\\(.+\\)\\'" mime-type))
|
||||||
|
(let ((sub (downcase (match-string 1 mime-type))))
|
||||||
|
(or (cdr (assoc sub mu4e--mime-subtype-extension-alist))
|
||||||
|
(replace-regexp-in-string "\\`x-" "" sub)))))
|
||||||
|
|
||||||
(defun mu4e-mime-type-to-icon (mime-type)
|
(defun mu4e-mime-type-to-icon (mime-type)
|
||||||
"Return an icon string for MIME-TYPE, or nil.
|
"Return an icon string for MIME-TYPE, or nil.
|
||||||
|
|
||||||
Uses `mu4e-mime-type-to-icon-function' if set; otherwise
|
Uses `mu4e-mime-type-to-icon-function' if set; otherwise
|
||||||
falls back to `mu4e-file-name-to-icon' with a dummy filename
|
falls back to `mu4e-file-name-to-icon' with a dummy filename
|
||||||
derived from the MIME type via `mailcap-mime-type-to-extension'."
|
derived from the MIME type's subtype (e.g. `image/png' yields
|
||||||
|
`file.png'), consulting `mu4e--mime-subtype-extension-alist'
|
||||||
|
for known quirks like `text/plain' -> `txt'."
|
||||||
(when mime-type
|
(when mime-type
|
||||||
(or (and mu4e-mime-type-to-icon-function
|
(or (and mu4e-mime-type-to-icon-function
|
||||||
(funcall mu4e-mime-type-to-icon-function mime-type))
|
(funcall mu4e-mime-type-to-icon-function mime-type))
|
||||||
(let ((ext (mailcap-mime-type-to-extension mime-type)))
|
(when-let* ((ext (mu4e--mime-type-extension mime-type)))
|
||||||
(when ext
|
(mu4e-file-name-to-icon (concat "file." ext))))))
|
||||||
(mu4e-file-name-to-icon
|
|
||||||
(concat "file." (symbol-name ext))))))))
|
|
||||||
|
|
||||||
;;; Messages, warnings and errors
|
;;; Messages, warnings and errors
|
||||||
(defun mu4e-format (frm &rest args)
|
(defun mu4e-format (frm &rest args)
|
||||||
|
|||||||
@ -76,6 +76,26 @@ See `mu4e--uniquify-file-name' for an example."
|
|||||||
(defvar-local mu4e--view-mime-parts nil
|
(defvar-local mu4e--view-mime-parts nil
|
||||||
"Cached MIME parts for this message.")
|
"Cached MIME parts for this message.")
|
||||||
|
|
||||||
|
(defun mu4e--mime-part-encoded-size (handle)
|
||||||
|
"Size in bytes of MIME-part HANDLE as it appears in the message.
|
||||||
|
This is the raw, still-encoded size (e.g. including base64 overhead)."
|
||||||
|
(with-current-buffer (mm-handle-buffer handle)
|
||||||
|
(- (point-max) (point-min))))
|
||||||
|
|
||||||
|
(defun mu4e--mime-part-decoded-size-approx (handle)
|
||||||
|
"Approximate decoded size in bytes of MIME-part HANDLE.
|
||||||
|
|
||||||
|
Computed from the encoded size *without* actually decoding the
|
||||||
|
part. For base64 the result is 3/4 of the encoded size; for other
|
||||||
|
encodings the encoded size is used as-is, which is accurate for
|
||||||
|
identity encodings (7bit/8bit/binary) and a reasonable
|
||||||
|
approximation for quoted-printable."
|
||||||
|
(let ((encoded-size (mu4e--mime-part-encoded-size handle))
|
||||||
|
(encoding (mm-handle-encoding handle)))
|
||||||
|
(pcase (and encoding (downcase (format "%s" encoding)))
|
||||||
|
("base64" (/ (* encoded-size 3) 4))
|
||||||
|
(_ encoded-size))))
|
||||||
|
|
||||||
(defun mu4e-view-mime-parts()
|
(defun mu4e-view-mime-parts()
|
||||||
"Get the list of MIME parts for this message.
|
"Get the list of MIME parts for this message.
|
||||||
The list is a list of plists, one for each MIME-part.
|
The list is a list of plists, one for each MIME-part.
|
||||||
@ -85,9 +105,15 @@ The plists have the properties:
|
|||||||
:part-index : Gnus index number
|
:part-index : Gnus index number
|
||||||
:mime-type : MIME-type (string) or nil
|
:mime-type : MIME-type (string) or nil
|
||||||
:encoding : Content encoding (string) or nil
|
:encoding : Content encoding (string) or nil
|
||||||
:disposition : Content disposition (attachment\" or inline\") or nil
|
:disposition : Content disposition (\"attachment\" or \"inline\")
|
||||||
|
or nil
|
||||||
:filename : The file name if it has one, or an invented one
|
:filename : The file name if it has one, or an invented one
|
||||||
otherwise
|
otherwise
|
||||||
|
:encoded-size : Size in bytes of the part as it appears in the
|
||||||
|
message (still encoded)
|
||||||
|
:decoded-size-approx: Approximate decoded size in bytes, computed from
|
||||||
|
the encoded size without actually decoding the
|
||||||
|
part (see `mu4e--mime-part-decoded-size-approx')
|
||||||
|
|
||||||
There are some internal fields as well, e.g. ; subject to change:
|
There are some internal fields as well, e.g. ; subject to change:
|
||||||
|
|
||||||
@ -120,6 +146,9 @@ This uses `gnus-article-mime-handle-alist'."
|
|||||||
;; XXX perhaps guess extension based on mime-type
|
;; XXX perhaps guess extension based on mime-type
|
||||||
:filename ,(or fname
|
:filename ,(or fname
|
||||||
(format "mime-part-%02d" index))
|
(format "mime-part-%02d" index))
|
||||||
|
:encoded-size ,(mu4e--mime-part-encoded-size handle)
|
||||||
|
:decoded-size-approx
|
||||||
|
,(mu4e--mime-part-decoded-size-approx handle)
|
||||||
:target-dir ,(mu4e-determine-attachment-dir
|
:target-dir ,(mu4e-determine-attachment-dir
|
||||||
fname mime-type)
|
fname mime-type)
|
||||||
;; 'attachment-like' just means it has its own
|
;; 'attachment-like' just means it has its own
|
||||||
@ -194,11 +223,21 @@ COMPLETIONS is the list of completion strings to affixate."
|
|||||||
'face 'mu4e-header-key-face))
|
'face 'mu4e-header-key-face))
|
||||||
(mimetype (propertize (or (plist-get part :mime-type) "")
|
(mimetype (propertize (or (plist-get part :mime-type) "")
|
||||||
'face 'mu4e-header-value-face))
|
'face 'mu4e-header-value-face))
|
||||||
|
(size (propertize
|
||||||
|
(file-size-human-readable
|
||||||
|
(or (plist-get part :decoded-size-approx) 0))
|
||||||
|
'face 'mu4e-system-face))
|
||||||
(target (propertize (or (plist-get part :target-dir) "")
|
(target (propertize (or (plist-get part :target-dir) "")
|
||||||
'face 'mu4e-system-face))
|
'face 'mu4e-system-face))
|
||||||
(icon (or (mu4e-mime-type-to-icon
|
;; Prefer the real filename: `nerd-icons' matches filenames
|
||||||
(plist-get part :mime-type))
|
;; against regexes, so a specific name like `foo.patch' picks
|
||||||
(mu4e-file-name-to-icon raw-filename)))
|
;; a better icon than the dummy `file.patch' derived from the
|
||||||
|
;; MIME type. Only fall back to the MIME type when there's
|
||||||
|
;; no filename.
|
||||||
|
(icon (or (and (> (length raw-filename) 0)
|
||||||
|
(mu4e-file-name-to-icon raw-filename))
|
||||||
|
(mu4e-mime-type-to-icon
|
||||||
|
(plist-get part :mime-type))))
|
||||||
(prefix (if icon (concat icon " ") ""))
|
(prefix (if icon (concat icon " ") ""))
|
||||||
(suffix
|
(suffix
|
||||||
(pcase type
|
(pcase type
|
||||||
@ -208,6 +247,8 @@ COMPLETIONS is the list of completion strings to affixate."
|
|||||||
(length (format "%s" candidate))) ?\s)
|
(length (format "%s" candidate))) ?\s)
|
||||||
(format "%20s" mimetype)
|
(format "%20s" mimetype)
|
||||||
" "
|
" "
|
||||||
|
(format "%8s" size)
|
||||||
|
" "
|
||||||
(format "%s" (concat "-> " target))))
|
(format "%s" (concat "-> " target))))
|
||||||
('mime-part
|
('mime-part
|
||||||
(concat
|
(concat
|
||||||
@ -217,6 +258,8 @@ COMPLETIONS is the list of completion strings to affixate."
|
|||||||
(length filename)) ?\s)
|
(length filename)) ?\s)
|
||||||
(format "%20s" mimetype)
|
(format "%20s" mimetype)
|
||||||
" "
|
" "
|
||||||
|
(format "%8s" size)
|
||||||
|
" "
|
||||||
(format "%s" (concat "-> " target))))
|
(format "%s" (concat "-> " target))))
|
||||||
(_ (mu4e-error "Unsupported annotation type %s" type)))))
|
(_ (mu4e-error "Unsupported annotation type %s" type)))))
|
||||||
(list candidate prefix suffix)))
|
(list candidate prefix suffix)))
|
||||||
|
|||||||
Reference in New Issue
Block a user