mu4e-headers/search: use last-query information

Use the information from the last-query to update the modeline,
sorting-column etc.
This commit is contained in:
Dirk-Jan C. Binnema
2025-04-12 15:23:52 +07:00
parent f5b9cf4cfb
commit a6d68a4146
2 changed files with 65 additions and 50 deletions

View File

@ -384,8 +384,6 @@ Returns the docid, or nil if there is none."
(goto-char point)) (goto-char point))
(get-text-property (line-beginning-position) 'docid))) (get-text-property (line-beginning-position) 'docid)))
(defun mu4e~headers-goto-docid (docid &optional to-mark) (defun mu4e~headers-goto-docid (docid &optional to-mark)
"Go to beginning of the line with DOCID. "Go to beginning of the line with DOCID.
Nil if it cannot be found. If the optional TO-MARK is Nil if it cannot be found. If the optional TO-MARK is
@ -1032,51 +1030,66 @@ COUNT is the number of messages found."
))) )))
;;; Headers-mode and mode-map ;;; Headers-mode and mode-map
(defun mu4e~header-line-click (sortable threads)
"Return function for header-line clicks.
If SORTABLE, handle the sorting; otherwise show a message that
you cannot sort by this field. If THREADS, give a more
informative error message."
(if (not sortable)
(if threads
(lambda (&optional e)
(interactive "e")
(mu4e-message "With threading, you can only sort by date"))
(lambda (&optional e)
(interactive "e")
(mu4e-message "Field is not sortable")))
(lambda (&optional e)
(interactive "e")
;; getting the field, inspired by
;; `tabulated-list-col-sort'
(let* ((obj (posn-object (event-start e)))
(field (and obj
(get-text-property 0 'field (car obj)))))
;; "t": if we're already sorted by field, the
;; sort-order is changed
(mu4e-search-change-sorting field t)))))
(defun mu4e~header-line-format () (defun mu4e~header-line-format ()
"Get the format for the header line." "Get the format for the header line."
(let ((uparrow (if mu4e-use-fancy-chars "" " ^")) (let* ((plist (mu4e-server-last-query)) ;; info about the last query
(downarrow (if mu4e-use-fancy-chars "" " V"))) (reverse (plist-get plist :reverse))
(threads (plist-get plist :threads))
;; with threads enabled, we can only sort by ;date
(sort-field (if threads :date (plist-get plist :sort-field)))
(fields (append mu4e-header-info mu4e-header-info-custom))
(arrow (if reverse
(if mu4e-use-fancy-chars "" " V")
(if mu4e-use-fancy-chars "" " ^"))))
(cons (cons
(make-string (make-string
(+ mu4e--mark-fringe-len (floor (fringe-columns 'left t))) ?\s) (+ mu4e--mark-fringe-len (floor (fringe-columns 'left t))) ?\s)
(mapcar (mapcar
(lambda (item) (lambda (item)
(let* (;; with threading enabled, we're necessarily sorting by date. (let* ((field (car item)) (width (cdr item))
(sort-field (if mu4e-search-threads (info (cdr (assoc field fields)))
:date mu4e-search-sort-field)) (sortable-info (plist-get info :sortable))
(field (car item)) (width (cdr item)) ;; the effective sort-field for this field is as per its info;
(info (cdr (assoc field ;; if t, it's the field itself; otherwise it's either some
(append mu4e-header-info ;; _other_ field (for fields which are sorted by some other field), or nil
mu4e-header-info-custom)))) ;; for fields that cannot be sorted.
(sortable (plist-get info :sortable)) (field-sort-field
;; if sortable, it is either t (when field is sortable itself) (if (eq sortable-info t) field sortable-info))
;; or a symbol (if another field is used for sorting) ;; only if we're actually looking at if for this column
(this-field (when sortable (if (booleanp sortable) (field-sort-field (and (eq field-sort-field sort-field) field-sort-field))
field
sortable)))
(help (plist-get info :help)) (help (plist-get info :help))
;; triangle to mark the sorted-by column ;; triangle to mark the sorted-by column
(arrow (arrow
(when (and sortable (eq this-field sort-field)) (when field-sort-field
(if (eq mu4e-search-sort-direction 'descending) (if reverse downarrow uparrow)))
downarrow
uparrow)))
(name (concat (plist-get info :shortname) arrow)) (name (concat (plist-get info :shortname) arrow))
(map (make-sparse-keymap))) (map (make-sparse-keymap)))
(when sortable
(define-key map [header-line mouse-1] (define-key map [header-line mouse-1]
(lambda (&optional e) (mu4e~header-line-click field-sort-field threads))
;; getting the field, inspired by
;; `tabulated-list-col-sort'
(interactive "e")
(let* ((obj (posn-object (event-start e)))
(field
(and obj
(get-text-property 0 'field (car obj)))))
;; "t": if we're already sorted by field, the
;; sort-order is changed
(mu4e-search-change-sorting field t)))))
(concat (concat
(propertize (propertize
(if width (if width
@ -1085,8 +1098,8 @@ COUNT is the number of messages found."
name) name)
'face (when arrow 'bold) 'face (when arrow 'bold)
'help-echo help 'help-echo help
'mouse-face (when sortable 'highlight) 'mouse-face (when field-sort-field 'highlight)
'keymap (when sortable map) 'keymap (when field-sort-field map)
'field field) " "))) 'field field) " ")))
mu4e-headers-fields)))) mu4e-headers-fields))))

View File

@ -96,15 +96,17 @@ Example that hides all trashed messages:
This can be used to toggle use of the predicate through This can be used to toggle use of the predicate through
`mu4e-search-toggle-property'.") `mu4e-search-toggle-property'.")
(defcustom mu4e-search-sort-field :date (defcustom mu4e-search-sort-field :date
"Field to sort the headers by. A symbol: "Field to sort the headers by.
one of: `:date', `:subject', `:size', `:prio', `:from', `:to.',
`:list'.
Note that when threading is enabled (through You can use any field from `mu4e-header-info' with a non-nil
`mu4e-search-threads'), the headers are exclusively sorted `:sortable' in their information.
chronologically (`:date') by the newest message in the thread."
The ':'-prefix is there for historical reasons.
When threading is enabled (through `mu4e-search-threads'), the
headers are exclusively sorted chronologically (`:date') by the
newest message in the thread."
:type '(radio (const :date) :type '(radio (const :date)
(const :subject) (const :subject)
(const :size) (const :size)
@ -112,6 +114,8 @@ chronologically (`:date') by the newest message in the thread."
(const :from) (const :from)
(const :to) (const :to)
(const :list)) (const :list))
;; this is not exhaustive; but the other ones don't make
;; much sense as default sort-fields.
:group 'mu4e-search) :group 'mu4e-search)
(defcustom mu4e-search-sort-direction 'descending (defcustom mu4e-search-sort-direction 'descending
@ -528,20 +532,18 @@ last search with the new setting."
(defun mu4e--search-modeline-item () (defun mu4e--search-modeline-item ()
"Get mu4e-search modeline item." "Get mu4e-search modeline item."
(let* ((label (lambda (label-cons) (let* ((pinfo (mu4e-server-last-query))
(label (lambda (label-cons)
(if mu4e-use-fancy-chars (if mu4e-use-fancy-chars
(cdr label-cons) (car label-cons)))) (cdr label-cons) (car label-cons))))
(props (props
`((,mu4e-search-full ,mu4e-search-full-label `((,mu4e-search-full ,mu4e-search-full-label
"Full search") "Full search")
(,mu4e-search-include-related (,(plist-get pinfo :related) ,mu4e-search-related-label
,mu4e-search-related-label
"Include related messages") "Include related messages")
(,mu4e-search-threads (,(plist-get pinfo :threads) ,mu4e-search-threaded-label
,mu4e-search-threaded-label
"Show message threads") "Show message threads")
(,mu4e-search-skip-duplicates (,(plist-get pinfo :skip-dups) ,mu4e-search-skip-duplicates-label
,mu4e-search-skip-duplicates-label
"Skip duplicate messages") "Skip duplicate messages")
(,mu4e-search-hide-enabled (,mu4e-search-hide-enabled
,mu4e-search-hide-label ,mu4e-search-hide-label