Commit c34de23fdc28

Vincent Demeester <vincent@sbr.pm>
2025-03-06 17:11:41
tools/emacs: re-add recent consult-mu code
I may want to use submodules instead but… Signed-off-by: Vincent Demeester <vincent@sbr.pm>
1 parent 72d601b
Changed files (2)
tools/emacs/lisp/consult-mu-embark.el
@@ -0,0 +1,249 @@
+;;; consult-mu-embark.el --- Emabrk Actions for consult-mu -*- lexical-binding: t -*-
+
+;; Copyright (C) 2021-2023
+
+;; Author: Armin Darvish
+;; Maintainer: Armin Darvish
+;; Created: 2023
+;; Version: 1.0
+;; Package-Requires: ((emacs "28.0") (consult "2.0"))
+;; Homepage: https://github.com/armindarvish/consult-mu
+;; Keywords: convenience, matching, tools, email
+;; Homepage: https://github.com/armindarvish/consult-mu
+
+;; SPDX-License-Identifier: GPL-3.0-or-later
+
+;; This file is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published
+;; by the Free Software Foundation, either version 3 of the License,
+;; or (at your option) any later version.
+;;
+;; This file is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with this file.  If not, see <https://www.gnu.org/licenses/>.
+
+
+;;; Commentary:
+
+;; This package provides an alternative interactive serach interface for
+;; mu and mu4e (see URL `https://djcbsoftware.nl/code/mu/mu4e.html').
+;; It uses a consult-based minibuffer completion for searching and
+;; selecting, and marking emails, as well as additional utilities for
+;; composing emails and more.
+
+;;  This package requires mu4e version "1.10.8" or later.
+
+;;; Code:
+
+;;; Requirements
+(require 'embark)
+(require 'consult-mu)
+
+;;; Customization Variables
+(defcustom consult-mu-embark-noconfirm-before-execute nil
+  "Should consult-mu-embark skip confirmation when executing marks?"
+  :group 'consult-mu
+  :type 'boolean)
+
+;;; Define Embark Action Functions
+(defun consult-mu-embark-default-action (cand)
+  "Run `consult-mu-action' on the candidate, CAND."
+  (let* ((msg (get-text-property 0 :msg cand))
+         (query (get-text-property 0 :query cand))
+         (type (get-text-property 0 :type cand))
+         (newcand (cons cand `(:msg ,msg :query ,query :type ,type))))
+    (if (equal type :async)
+        (consult-mu--update-headers query t msg :async))
+    (funcall consult-mu-action newcand)))
+
+
+
+(defun consult-mu-embark-reply (cand)
+  "Reply to message in CAND."
+  (let* ((msg (get-text-property 0 :msg cand))
+         (query (get-text-property 0 :query cand))
+         (type (get-text-property 0 :type cand)))
+    (if (equal type :async)
+        (consult-mu--update-headers query t msg :async))
+    (consult-mu--reply msg nil)))
+
+(defun consult-mu-embark-wide-reply (cand)
+  "Reply all for message in CAND."
+  (let* ((msg (get-text-property 0 :msg cand))
+         (query (get-text-property 0 :query cand))
+         (type (get-text-property 0 :type cand)))
+    (if (equal type :async)
+        (consult-mu--update-headers query t msg :async))
+    (consult-mu--reply msg )))
+
+(defun consult-mu-embark-forward (cand)
+  "Forward the message in CAND."
+  (let* ((msg (get-text-property 0 :msg cand))
+         (query (get-text-property 0 :query cand))
+         (type (get-text-property 0 :type cand)))
+    (if (equal type :async)
+        (consult-mu--update-headers query t msg :async))
+    (consult-mu--forward msg)))
+
+(defun consult-mu-embark-kill-message-field (cand)
+  "Get a header field of message in CAND."
+  (let* ((msg (get-text-property 0 :msg cand))
+         (query (get-text-property 0 :query cand))
+         (type (get-text-property 0 :type cand))
+         (msg-id (plist-get msg :message-id)))
+    (if (equal type :async)
+        (consult-mu--update-headers query t msg :async))
+    (with-current-buffer consult-mu-headers-buffer-name
+      (unless (equal (mu4e-message-field-at-point :message-id) msg-id)
+        (mu4e-headers-goto-message-id msg-id))
+      (if (equal (mu4e-message-field-at-point :message-id) msg-id)
+          (progn
+            (mu4e~headers-update-handler msg nil nil))))
+
+    (with-current-buffer consult-mu-view-buffer-name
+      (kill-new (consult-mu--message-get-header-field))
+      (consult-mu--pulse-region (point) (line-end-position)))))
+
+(defun consult-mu-embark-save-attachmnts (cand)
+  "Save attachments of CAND."
+  (let* ((msg (get-text-property 0 :msg cand))
+         (query (get-text-property 0 :query cand))
+         (type (get-text-property 0 :type cand))
+         (msg-id (plist-get msg :message-id)))
+
+    (if (equal type :async)
+        (consult-mu--update-headers query t msg :async))
+
+    (with-current-buffer consult-mu-headers-buffer-name
+      (unless (equal (mu4e-message-field-at-point :message-id) msg-id)
+        (mu4e-headers-goto-message-id msg-id))
+      (if (equal (mu4e-message-field-at-point :message-id) msg-id)
+          (progn
+            (mu4e~headers-update-handler msg nil nil))))
+
+    (with-current-buffer consult-mu-view-buffer-name
+      (goto-char (point-min))
+      (re-search-forward "^\\(Attachment\\|Attachments\\): " nil t)
+      (consult-mu--pulse-region (point) (line-end-position))
+      (mu4e-view-save-attachments t))))
+
+(defun consult-mu-embark-search-messages-from-contact (cand)
+  "Search messages from the same sender as the message in CAND."
+  (let* ((msg (get-text-property 0 :msg cand))
+         (from (car (plist-get msg :from)))
+         (email (plist-get from :email)))
+    (consult-mu (concat "from:" email))))
+
+(defun consult-mu-embark-search-messages-with-subject (cand)
+  "Search all messages for the same subject as the message in CAND."
+  (let* ((msg (get-text-property 0 :msg cand))
+         ;;(subject (replace-regexp-in-string ":\\|#\\|\\.\\|\\+" "" (plist-get msg :subject)))
+         (subject (replace-regexp-in-string ":\\|#\\|\\.\\|\\+\\|\\(\\[.*\\]\\)" "" (format "%s" (plist-get msg :subject)))))
+    (consult-mu (concat "subject:" subject))))
+
+;; macro for defining functions for marks
+(defmacro consult-mu-embark--defun-mark-for (mark)
+  "Define a function mu4e-view-mark-for- MARK."
+  (let ((funcname (intern (format "consult-mu-embark-mark-for-%s" mark)))
+        (docstring (format "Mark the current message for %s." mark)))
+    `(progn
+       (defun ,funcname (cand) ,docstring
+              (let* ((msg (get-text-property 0 :msg cand))
+                     (msgid (plist-get msg  :message-id))
+                     (query (get-text-property 0 :query cand))
+                     (buf (get-buffer consult-mu-headers-buffer-name)))
+                (if buf
+                    (progn
+                      (with-current-buffer buf
+                        (if (eq major-mode 'mu4e-headers-mode)
+                            (progn
+                              (goto-char (point-min))
+                              (mu4e-headers-goto-message-id msgid)
+                              (if (equal (mu4e-message-field-at-point :message-id) msgid)
+                                  (mu4e-headers-mark-and-next ',mark)
+                                (progn
+                                  (consult-mu--update-headers query t msg :async)
+                                  (with-current-buffer buf
+                                    (goto-char (point-min))
+                                    (mu4e-headers-goto-message-id msgid)
+                                    (if (equal (mu4e-message-field-at-point :message-id) msgid)
+                                        (mu4e-headers-mark-and-next ',mark))))))
+                          (progn
+                            (consult-mu--update-headers query t msg :async)
+                            (with-current-buffer buf
+                              (goto-char (point-min))
+                              (mu4e-headers-goto-message-id msgid)
+                              (if (equal (mu4e-message-field-at-point :message-id) msgid)
+                                  (mu4e-headers-mark-and-next ',mark)))))))))))))
+
+;; add embark functions for marks
+(defun consult-mu-embark--defun-func-for-marks (marks)
+  "Run the macro `consult-mu-embark--defun-mark-for' on MARKS.
+
+MARKS is a list of marks.
+
+This is useful for creating embark functions for all the `mu4e-marks'
+elements."
+  (mapcar (lambda (mark) (eval `(consult-mu-embark--defun-mark-for ,mark))) marks))
+
+;; use consult-mu-embark--defun-func-for-marks to make a function for each `mu4e-marks' element.
+(consult-mu-embark--defun-func-for-marks (mapcar 'car mu4e-marks))
+
+;;; Define Embark Keymaps
+(defvar-keymap consult-mu-embark-general-actions-map
+  :doc "Keymap for consult-mu-embark"
+  :parent embark-general-map)
+
+(add-to-list 'embark-keymap-alist '(consult-mu . consult-mu-embark-general-actions-map))
+
+
+(defvar-keymap consult-mu-embark-messages-actions-map
+  :doc "Keymap for consult-mu-embark-messages"
+  :parent consult-mu-embark-general-actions-map
+  "r" #'consult-mu-embark-reply
+  "w" #'consult-mu-embark-wide-reply
+  "f" #'consult-mu-embark-forward
+  "?" #'consult-mu-embark-kill-message-field
+  "c" #'consult-mu-embark-search-messages-from-contact
+  "s" #'consult-mu-embark-search-messages-with-subject
+  "S" #'consult-mu-embark-save-attachmnts)
+
+(add-to-list 'embark-keymap-alist '(consult-mu-messages . consult-mu-embark-messages-actions-map))
+
+
+;; add mark keys to `consult-mu-embark-messages-actions-map' keymap
+(defun consult-mu-embark--add-keys-for-marks (marks)
+  "Add a key for each mark in MARKS to embark map.
+
+Adds the keys in `consult-mu-embark-messages-actions-map', and binds the
+combination “m key”, where key is the :char in mark plist in the
+`consult-mu-embark-messages-actions-map' to the function defined by the
+prefix “consult-mu-embark-mark-for-” and mark.
+
+This is useful for adding all `mu4e-marks' to embark key bindings under a
+submenu (called by “m”), for example, the default mark-for-archive mark
+that is bound to r in mu4e buffers can be called in embark by “m r”."
+  (mapcar (lambda (mark)
+            (let* ((key (plist-get (cdr mark) :char))
+                   (key (cond ((consp key) (car key)) ((stringp key) key)))
+                   (func (intern (concat "consult-mu-embark-mark-for-" (format "%s" (car mark)))))
+                   (key (concat "m" key)))
+              (define-key consult-mu-embark-messages-actions-map key func)))
+          marks))
+
+;; add all `mu4e-marks to embark keybindings. See `consult-mu-embark--add-keys-for-marks' above for more details
+(consult-mu-embark--add-keys-for-marks mu4e-marks)
+
+;; change the default action on `consult-mu-messages' category.
+(add-to-list 'embark-default-action-overrides '(consult-mu-messages . consult-mu-embark-default-action))
+
+
+;;; Provide `consult-mu-embark' module
+
+(provide 'consult-mu-embark)
+
+;;; consult-mu-embark.el ends here
tools/emacs/lisp/consult-mu.el
@@ -0,0 +1,1673 @@
+;;; consult-mu.el --- Consult Mu4e asynchronously -*- lexical-binding: t -*-
+
+;; Copyright (C) 2023 Armin Darvish
+
+;; Author: Armin Darvish
+;; Maintainer: Armin Darvish
+;; Created: 2023
+;; Version: 1.0
+;; Package-Requires: ((emacs "28.0") (consult "2.0"))
+;; Keywords: convenience, matching, tools, email
+;; Homepage: https://github.com/armindarvish/consult-mu
+
+;; SPDX-License-Identifier: GPL-3.0-or-later
+
+;; This file is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published
+;; by the Free Software Foundation, either version 3 of the License,
+;; or (at your option) any later version.
+;;
+;; This file is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with this file.  If not, see <https://www.gnu.org/licenses/>.
+
+
+;;; Commentary:
+
+;; This package provides an alternative interactive serach interface for
+;; mu and mu4e (see URL `https://djcbsoftware.nl/code/mu/mu4e.html').
+;; It uses a consult-based minibuffer completion for searching and
+;; selecting, and marking emails, as well as additional utilities for
+;; composing emails and more.
+
+;;  This package requires mu4e version "1.10.8" or later.
+
+;;; Code:
+
+;;; Requirements
+(require 'consult)
+(require 'mu4e)
+
+;;; Group
+
+(defgroup consult-mu nil
+  "Options for `consult-mu'."
+  :group 'convenience
+  :group 'minibuffer
+  :group 'consult
+  :group 'mu4e
+  :prefix "consult-mu-")
+
+;;; Customization Variables
+
+(defcustom consult-mu-args '("mu")
+  "Command line arguments to call `mu` asynchronously.
+
+The dynamically computed arguments are appended.
+Can be either a string, or a list of strings or expressions."
+  :group 'consult-mu
+  :type '(choice string (repeat (choice string sexp))))
+
+(defcustom consult-mu-maxnum mu4e-search-results-limit
+  "Maximum number of results.
+
+This is normally passed to “--maxnum” in the command line or is defined by
+`mu4e-search-results-limit'.  By default inherits from
+`mu4e-search-results-limit'."
+  :group 'consult-mu
+  :type '(choice (const :tag "Unlimited" -1)
+                 (integer :tag "Limit")))
+
+(defcustom consult-mu-search-sort-field mu4e-search-sort-field
+  "What field to sort results by?
+
+By defualt inherits from `mu4e-search-sort-field'."
+  :group 'consult-mu
+  :type '(radio (const :tag "Date" :date)
+                (const :tag "Subject" :subject)
+                (const :tag "File Size" :size)
+                (const :tag "Priority" :prio)
+                (const :tag "From (Sender)" :from)
+                (const :tag "To (Recipients)" :to)
+                (const :tag "Mailing List" :list)))
+
+(defcustom consult-mu-headers-fields mu4e-headers-fields
+  "A list of header fields to show in the headers buffer.
+
+By default inherits from `mu4e-headers-field'.
+
+From mu4e docs:
+
+Each element has the form (HEADER . WIDTH), where HEADER is one of
+the available headers (see `mu4e-header-info') and WIDTH is the
+respective width in characters.
+
+A width of nil means “unrestricted”, and this is best reserved
+for the rightmost \(last\) field.  Note that Emacs may become very
+slow with excessively long lines \(1000s of characters\), so if you
+regularly get such messages, you want to avoid fields with nil
+altogether."
+  :group 'consult-mu
+  :type `(repeat (cons (choice ,@(mapcar (lambda (h)
+                                           (list 'const
+                                                 :tag (plist-get (cdr h) :help)
+                                                 (car h)))
+                                         mu4e-header-info))
+                       (choice (integer :tag "width")
+                               (const :tag "unrestricted width" nil)))))
+
+(defcustom consult-mu-headers-template nil
+  "A template string to make custom header formats.
+
+If non-nil, `consult-mu' uses this string to format the headers instead of
+`consult-mu-headers-field'.
+
+The string should be of the format “%[char][integer]%[char][integer]...”,
+and allow dynamic insertion of the content.  Each “%[char][integer]“ chunk
+represents a different field and the integer defines the length of the
+field.
+
+The list of available fields are:
+
+  %f  sender(s) \(e.g. from: field of email\)
+  %t  receivers(s) \(i.e. to: field of email\)
+  %s  subject \(i.e. title of email\)
+  %d  date \(i.e. the date email was sent/received\)
+  %p  priority
+  %z  size
+  %i  message-id \(as defined by mu\)
+  %g  flags \(as defined by mu\)
+  %G  pretty flags \(this uses `mu4e~headers-flags-str' to pretify flags\)
+  %x  tags \(as defined by mu\)
+  %c  cc \(i.e. cc: field of the email\)
+  %h  bcc \(i.e. bcc: field of the email\)
+  %r  date chaged \(as defined by :changed in mu4e\)
+
+For exmaple, “%d15%s50” means 15 characters for date and 50 charcters for
+subject, and “%d13%s37%f17” would make a header containing 13 characters
+for Date, 37 characters for Subject, and 20 characters for From field,
+making a header that looks like this:
+
+Thu 09 Nov 23  Title of the Email Limited to 50 Char...  example@domain..."
+  :group 'consult-mu
+  :type '(choice (const :tag "Fromatted String" :format "%{%%d13%%s50%%f17%}")
+                 (function :tag "Custom Function")))
+
+(defcustom consult-mu-search-sort-direction mu4e-search-sort-direction
+  "Direction to sort by a symbol.
+
+By defualt inherits from `mu4e-search-sort-direction', and can either be
+\='descending (sorting  Z->A) or \='ascending (sorting A->Z)."
+
+  :group 'consult-mu
+  :type '(radio (const ascending)
+                (const descending)))
+
+
+(defcustom consult-mu-search-threads mu4e-search-threads
+  "Whether to calculate threads for search results.
+
+By defualt inherits from `mu4e-search-threads'.
+
+Note that per mu4e docs:
+When threading is enabled, the headers are exclusively sorted
+chronologically (:date) by the newest message in the thread."
+  :group 'consult-mu
+  :type 'boolean)
+
+(defcustom consult-mu-group-by nil
+  "What field to use to group the results in the minibuffer.
+
+By default it is set to :date, but can be any of:
+
+  :subject      group by subject
+  :from         group by the name/email the sender(s)
+  :to           group by name/email of the reciver(s)
+  :date         group by date
+  :time         group by the time of email \(i.e. hour, minute, seconds\)
+  :datetime     group by date and time of the email
+  :year         group by the year of the email \(i.e. 2023, 2022, ...\)
+  :month        group by the month of the email \(i.e. Jan, Feb, ..., Dec\)
+  :week         group by the week number of the email
+                \(i.e. 1st week, 2nd week, ... 52nd week\)
+  :day-of-week  group by the day email was sent (i.e. Mondays, Tuesdays, ...)
+  :day          group by the day email was sent (similar to :day-of-week)
+  :size         group by the file size of the email
+  :flags        group by flags (as defined by mu)
+  :tags         group by tags (as defined by mu)
+  :changed      group by the date changed
+                \(as defined by :changed field in mu4e\)"
+  :group 'consult-mu
+  :type '(radio (const :date)
+                (const :subject)
+                (const :from)
+                (const :to)
+                (const :time)
+                (const :datetime)
+                (const :year)
+                (const :month)
+                (const :week)
+                (const :day-of-week)
+                (const :day)
+                (const :size)
+                (const :flags)
+                (const :tags)
+                (const :changed)
+                (const nil)))
+
+(defcustom consult-mu-mark-previewed-as-read nil
+  "Whether to mark PREVIEWED emails as read or not?"
+  :group 'consult-mu
+  :type 'boolean)
+
+(defcustom consult-mu-mark-viewed-as-read t
+  "Whether to mark VIEWED emails as read or not?"
+  :group 'consult-mu
+  :type 'boolean)
+
+(defcustom consult-mu-headers-buffer-name "*consult-mu-headers*"
+  "Default name for HEADERS buffer explicitly for `consult-mu'.
+
+For more info see `mu4e-headers-buffer-name'."
+  :group 'consult-mu
+  :type 'string)
+
+(defcustom consult-mu-view-buffer-name "*consult-mu-view*"
+  "Default name for VIEW buffer explicitly for `consult-mu'.
+
+For more info see `mu4e-view-buffer-name'."
+  :group 'consult-mu
+  :type 'string)
+
+(defcustom consult-mu-preview-key consult-preview-key
+  "Preview key for `consult-mu'.
+
+This is similar to `consult-preview-key' but explicitly for `consult-mu'."
+  :group 'consult-mu
+  :type '(choice (symbol :tag "Any key" 'any)
+                 (list :tag "Debounced"
+                       (const :debounce)
+                       (float :tag "Seconds" 0.1)
+                       (const any))
+                 (const :tag "No preview" nil)
+                 (key :tag "Key")
+                 (repeat :tag "List of keys" key)))
+
+
+(defcustom consult-mu-highlight-matches t
+  "Should `consult-mu' highlight search queries in preview buffers?"
+  :group 'consult-mu
+  :type 'boolean)
+
+(defcustom consult-mu-use-wide-reply 'ask
+  "Reply to all or not?
+
+This defines whether `consult-mu--reply-action' should reply to all or not."
+  :group 'consult-mu
+  :type '(choice (symbol :tag "Ask for confirmation" 'ask)
+                 (const :tag "Do not reply to all" nil)
+                 (const :tag "Always reply to all" t)))
+
+(defcustom consult-mu-action #'consult-mu--view-action
+  "The function that is used when selecting a message.
+By default it is bound to `consult-mu--view-action'."
+  :group 'consult-mu
+  :type '(choice (function :tag "(Default) View Message in Mu4e Buffers" consult-mu--view-action)
+                 (function :tag "Reply to Message" consult-mu--reply-action)
+                 (function :tag "Forward Message" consult-mu--forward-action)
+                 (function :tag "Custom Function")))
+
+(defcustom consult-mu-default-command #'consult-mu-dynamic
+  "Which command should `consult-mu' call."
+  :group 'consult-mu
+  :type '(choice (function :tag "(Default) Use Dynamic Collection (i.e. `consult-mu-dynamic')" #'consult-mu-dynamic)
+                 (function :tag "Use Async Collection (i.e. `consult-mu-async')"  #'consult-mu-async)
+                 (function :tag "Custom Function")))
+
+;;; Other Variables
+(defvar consult-mu-category 'consult-mu
+  "Category symbol for the `consult-mu' package.")
+
+(defvar consult-mu-messages-category 'consult-mu-messages
+  "Category symbol for messages in `consult-mu' package.")
+
+(defvar consult-mu--view-buffers-list (list)
+  "List of currently open preview buffers for `consult-mu'.")
+
+(defvar consult-mu--history nil
+  "History variable for `consult-mu'.")
+
+(defvar consult-mu-delimiter "      "
+  "Delimiter to use for fields in mu command output.
+
+The idea is Taken from  https://github.com/seanfarley/counsel-mu.")
+
+(defvar consult-mu-saved-searches-dynamic (list)
+  "List of Favorite searches for `consult-mu-dynamic'.")
+
+(defvar consult-mu-saved-searches-async consult-mu-saved-searches-dynamic
+  "List of Favorite searches for `consult-mu-async'.")
+
+(defvar consult-mu--override-group nil
+  "Override grouping in `consult-mu' based on user input.")
+
+(defvar consult-mu--mail-headers '("Subject" "From" "To" "From/To" "Cc" "Bcc" "Reply-To" "Date" "Attachments" "Tags" "Flags" "Maildir" "Summary" "List" "Path" "Size" "Message-Id" "List-Id" "Changed")
+  "List of possible headers in a message.")
+
+;;; Faces
+
+(defface consult-mu-highlight-match-face
+  `((t :inherit 'consult-highlight-match))
+  "Highlight match face in `consult-mu' view buffer.
+
+By default inherits from `consult-highlight-match'.
+This is used to highlight matches of search queries in the minibufffer
+completion list.")
+
+(defface consult-mu-preview-match-face
+  `((t :inherit 'consult-preview-match))
+  "Preview match face in `consult-mu' preview buffers.
+
+By default inherits from `consult-preview-match'.
+This is used to highlight matches of search query terms in preview buffers
+\(i.e. `consult-mu-view-buffer-name'\).")
+
+(defface consult-mu-default-face
+  `((t :inherit 'default))
+  "Default face in `consult-mu' minibuffer annotations.
+
+By default inherits from `default' face.")
+
+(defface consult-mu-subject-face
+  `((t :inherit 'font-lock-keyword-face))
+  "Subject face in `consult-mu' minibuffer annotations.
+
+By default inherits from `font-lock-keyword-face'.")
+
+(defface consult-mu-sender-face
+  `((t :inherit 'font-lock-variable-name-face))
+  "Contact face in `consult-mu' minibuffer annotations.
+
+By default inherits from `font-lock-variable-name-face'.")
+
+(defface consult-mu-receiver-face
+  `((t :inherit 'font-lock-variable-name-face))
+  "Contact face in `consult-mu' minibuffer annotations.
+
+By default inherits from `font-lock-variable-name-face'.")
+
+(defface consult-mu-date-face
+  `((t :inherit 'font-lock-preprocessor-face))
+  "Date face in `consult-mu' minibuffer annotations.
+
+By default inherits from `font-lock-preprocessor-face'.")
+
+(defface consult-mu-count-face
+  `((t :inherit 'font-lock-string-face))
+  "Count face in `consult-mu' minibuffer annotations.
+
+By default inherits from `font-lock-string-face'.")
+
+(defface consult-mu-size-face
+  `((t :inherit 'font-lock-string-face))
+  "Size face in `consult-mu' minibuffer annotations.
+
+By default inherits from `font-lock-string-face'.")
+
+(defface consult-mu-tags-face
+  `((t :inherit 'font-lock-comment-face))
+  "Tags/Comments face in `consult-mu' minibuffer annotations.
+
+By default inherits from `font-lock-comment-face'.")
+
+(defface consult-mu-flags-face
+  `((t :inherit 'font-lock-function-call-face))
+  "Flags face in `consult-mu' minibuffer annotations.
+
+By default inherits from `font-lock-function-call-face'.")
+
+(defface consult-mu-url-face
+  `((t :inherit 'link))
+  "URL face in `consult-mu' minibuffer annotations;
+
+By default inherits from `link'.")
+
+(defun consult-mu--pulse-regexp (regexp)
+  "Find and pulse REGEXP."
+  (goto-char (point-min))
+  (while (re-search-forward regexp nil t)
+    (when-let* ((m (match-data))
+                (beg (car m))
+                (end (cadr m))
+                (ov (make-overlay beg end))
+                (pulse-delay 0.075))
+      (pulse-momentary-highlight-overlay ov 'highlight))))
+
+(defun consult-mu--pulse-region (beg end)
+  "Find and pulse region from BEG to END."
+  (let ((ov (make-overlay beg end))
+        (pulse-delay 0.075))
+    (pulse-momentary-highlight-overlay ov 'highlight)))
+
+(defun consult-mu--pulse-line ()
+  "Pulse line at point momentarily."
+  (let* ((pulse-delay 0.055)
+         (ov (make-overlay (car (bounds-of-thing-at-point 'line))
+                           (cdr (bounds-of-thing-at-point 'line)))))
+    (pulse-momentary-highlight-overlay ov 'highlight)))
+
+(defun consult-mu--set-string-width (string width &optional prepend)
+  "Set the STRING width to a fixed value, WIDTH.
+
+If the STRING is longer than WIDTH, it truncates the string and adds
+ellipsis, “...”.  If the string is shorter, it adds whitespace to the
+string.  If PREPEND is non-nil, it truncates or adds whitespace from the
+beginning of string, instead of the end."
+  (let* ((string (format "%s" string))
+         (w (string-width string)))
+    (when (< w width)
+      (if prepend
+          (setq string (format "%s%s" (make-string (- width w) ?\s) (substring string)))
+        (setq string (format "%s%s" (substring string) (make-string (- width w) ?\s)))))
+    (when (> w width)
+      (if prepend
+          (setq string (format "...%s" (substring string (- w (- width 3)) w)))
+        (setq string (format "%s..." (substring string 0 (- width (+ w 3)))))))
+    string))
+
+(defun consult-mu--justify-left (string prefix maxwidth)
+  "Set the width of  STRING+PREFIX justified from left.
+
+Use `consult-mu--set-string-width' to the width of the concatenate of
+STRING+PREFIX \(e.g. “(concat prefix string)”\) within MAXWIDTH.  This is
+used for aligning marginalia info in the minibuffer."
+  (let ((w (string-width prefix)))
+    (if (> maxwidth w)
+        (consult-mu--set-string-width string (- maxwidth w) t)
+      string)))
+
+(defun consult-mu--highlight-match (regexp str ignore-case)
+  "Highlight REGEXP in STR.
+
+If a REGEXP contains a capturing group, only the captured group is
+highlighted, otherwise, the whole match is highlighted.
+Case is ignored if IGNORE-CASE is non-nil.
+\(This is adapted from `consult--highlight-regexps'.\)"
+  (let ((i 0))
+    (while (and (let ((case-fold-search ignore-case))
+                  (string-match regexp str i))
+                (> (match-end 0) i))
+      (let ((m (match-data)))
+        (setq i (cadr m)
+              m (or (cddr m) m))
+        (while m
+          (when (car m)
+            (add-face-text-property (car m) (cadr m)
+                                    'consult-mu-highlight-match-face nil str))
+          (setq m (cddr m))))))
+  str)
+
+(defun consult-mu--overlay-match (match-str buffer ignore-case)
+  "Highlight MATCH-STR in BUFFER using an overlay.
+
+If IGNORE-CASE is non-nil, it uses case-insensitive match.
+
+This is used to highlight matches to use queries when viewing emails.  See
+`consult-mu-overlays-toggle' for toggling highligths on/off."
+  (with-current-buffer (or (get-buffer buffer) (current-buffer))
+    (remove-overlays (point-min) (point-max) 'consult-mu-overlay t)
+    (goto-char (point-min))
+    (let ((case-fold-search ignore-case))
+      (while (search-forward match-str nil t)
+        (when-let* ((m (match-data))
+                    (beg (car m))
+                    (end (cadr m))
+                    (overlay (make-overlay beg end)))
+          (overlay-put overlay 'consult-mu-overlay t)
+          (overlay-put overlay 'face 'consult-mu-highlight-match-face))))))
+
+(defun consult-mu-overlays-toggle (&optional buffer)
+  "Toggle overlay highlight in BUFFER.
+
+BUFFER defaults to `current-buffer'."
+  (interactive)
+  (let ((buffer (or buffer (current-buffer))))
+    (with-current-buffer buffer
+      (dolist (o (overlays-in (point-min) (point-max)))
+        (when (overlay-get o 'consult-mu-overlay)
+          (if (and (overlay-get o 'face) (eq (overlay-get o 'face) 'consult-mu-highlight-match-face))
+              (overlay-put o 'face nil)
+            (overlay-put o 'face 'consult-mu-highlight-match-face)))))))
+
+(defun consult-mu--format-date (string)
+  "Format the date STRING from mu output.
+
+STRING is the output form a mu command, for example:
+`mu find query --fields d`
+Returns the date in the format Day-of-Week Month Day Year Time
+\(e.g. Sat Nov 04 2023 09:46:54\)"
+  (let ((string (replace-regexp-in-string " " "0" string)))
+    (format "%s %s %s"
+            (substring string 0 10)
+            (substring string -4 nil)
+            (substring string 11 -4))))
+
+(defun consult-mu-flags-to-string (FLAG)
+  "Covert FLAGS, from mu output to strings.
+
+FLAG is the output form mu command in the terminal, for example:
+ `mu find query --fields g`.
+This function converts each character in FLAG to an expanded string of the
+flag and returns the list of these strings."
+  (cl-loop for c across FLAG
+           collect
+           (pcase (string c)
+             ("D" 'draft)
+             ("F" 'flagged)
+             ("N" 'new)
+             ("P" 'forwarded)
+             ("R" 'replied)
+             ("S" 'read)
+             ("T" 'trashed)
+             ("a" 'attachment)
+             ("x" 'encrrypted)
+             ("s" 'signed)
+             ("u" 'unread)
+             ("l" 'list)
+             ("q" 'personal)
+             ("c" 'calendar)
+             (_ nil))))
+
+(defun consult-mu--message-extract-email-from-string (string)
+  "Find and return the first email address in the STRING."
+  (when (stringp string)
+    (string-match "[a-zA-Z0-9\_\.\+\-]+@[a-zA-Z0-9\-]+\.[a-zA-Z0-9\-\.]+" string)
+    (match-string 0 string)))
+
+(defun consult-mu--message-emails-string-to-list (string)
+  "Convert comma-separated STRING of email addresses to a list."
+  (when (stringp string)
+    (remove '(" " "\s" "\t")
+            (mapcar #'consult-mu--message-extract-email-from-string
+                    (split-string string ",\\|;\\|\t" t)))))
+
+(defun consult-mu--message-get-header-field (&optional field)
+  "Retrive FIELD header from the message/mail in the current buffer."
+  (save-match-data
+    (save-excursion
+      (when (or (derived-mode-p 'message-mode)
+                (derived-mode-p 'mu4e-view-mode)
+                (derived-mode-p 'org-msg-edit-mode)
+                (derived-mode-p 'mu4e-compose-mode))
+        (let* ((case-fold-search t)
+               (header-regexp (mapconcat (lambda (str) (concat "\n" str ": "))
+                                        consult-mu--mail-headers "\\|"))
+               (field (or (downcase field)
+                          (downcase (consult--read consult-mu--mail-headers
+                                                   :prompt "Header Field: ")))))
+          (if (string-prefix-p "attachment" field) (setq field "\\(attachment\\|attachments\\)"))
+          (goto-char (point-min))
+          (message-goto-body)
+          (let* ((match (re-search-backward (concat "^" field ": \\(?1:[[:ascii:][:nonascii:]]*?\\)\n\\(.*?:\\|\n\\)") nil t))
+                 (str (if (and match (match-string 1)) (string-trim (match-string 1)))))
+            (if (string-empty-p str) nil str)))))))
+
+(defun consult-mu--headers-append-handler (msglst)
+  "Append one-line descriptions of messages in MSGLST.
+
+This is used to override `mu4e~headers-append-handler' to ensure that
+buffer handling is done right for `consult-mu'."
+  (with-current-buffer "*consult-mu-headers*"
+    (let ((inhibit-read-only t))
+      (seq-do
+       ;; I use mu4e-column-faces and it overrides the default append-handler. To get the same effect I check if mu4e-column-faces is active and enabled.
+       (if (and (featurep 'mu4e-column-faces) mu4e-column-faces-mode)
+           (lambda (msg)
+             (mu4e-column-faces--insert-header msg (point-max)))
+         (lambda (msg)
+           (mu4e~headers-insert-header msg (point-max))))
+       msglst))))
+
+(defun consult-mu--view-msg (msg &optional buffername)
+  "Display the message MSG in a buffer with BUFFERNAME.
+
+BUFFERNAME defaults to `consult-mu-view-buffer-name'.
+
+This s used to overrides `mu4e-view' to ensure that buffer handling is done
+right for `consult-mu'."
+  (let* ((linked-headers-buffer (mu4e-get-headers-buffer "*consult-mu-headers*" t))
+         (mu4e-view-buffer-name (or buffername consult-mu-view-buffer-name)))
+    (setq gnus-article-buffer (mu4e-get-view-buffer linked-headers-buffer t))
+    (with-current-buffer gnus-article-buffer
+      (let ((inhibit-read-only t))
+        (remove-overlays (point-min) (point-max) 'mu4e-overlay t)
+        (erase-buffer)
+        (insert-file-contents-literally
+         (mu4e-message-readable-path msg) nil nil nil t)
+        (setq-local mu4e--view-message msg)
+        (mu4e--view-render-buffer msg)
+        (mu4e-loading-mode 0)
+        (with-current-buffer linked-headers-buffer
+          (setq-local mu4e~headers-view-win (mu4e-display-buffer gnus-article-buffer nil)))
+        (run-hooks 'mu4e-view-rendered-hook)))))
+
+(defun consult-mu--headers-clear (&optional text)
+  "Clear the headers buffer and related data structures.
+
+Optionally, show TEXT.
+
+This is used to override `mu4e~headers-clear' to ensure that buffer
+handling is done right for `consult-mu'."
+  (setq mu4e~headers-render-start (float-time)
+        mu4e~headers-hidden 0)
+  (with-current-buffer "*consult-mu-headers*"
+    (let ((inhibit-read-only t))
+      (mu4e--mark-clear)
+      (erase-buffer)
+      (when text
+        (goto-char (point-min))
+        (insert (propertize text 'face 'mu4e-system-face 'intangible t))))))
+
+(defun consult-mu--set-mu4e-search-sortfield (opts)
+  "Dynamically set the `mu4e-search-sort-field' based on user input.
+
+Uses user input (i.e. from `consult-mu' command) to define the sort field.
+
+OPTS is the command line options for mu and can be set by entering options
+in the minibuffer input.  For more details, refer to `consult-grep' and
+consult async documentation.
+
+For example if the user enters the following in the minibuffer:
+
+“#query -- --maxnum 400 --sortfield from”
+
+`mu4e-search-sort-field' is set to :from
+
+Note that per mu4e docs:
+When threading is enabled, the headers are exclusively sorted
+chronologically (:date) by the newest message in the thread."
+  (let* ((sortfield (cond
+                     ((member "-s" opts) (nth (+ (cl-position "-s" opts :test 'equal) 1) opts))
+                     ((member "--sortfield" opts) (nth (+ (cl-position "--sortfield" opts :test 'equal) 1) opts))
+                     (t consult-mu-search-sort-field))))
+    (pcase sortfield
+      ('nil
+       consult-mu-search-sort-field)
+      ((or "date" "d")
+       :date)
+      ((or "subject" "s")
+       :subject)
+      ((or "size" "z")
+       :size)
+      ((or "prio" "p")
+       :prio)
+      ((or "from" "f")
+       :from)
+      ((or "to" "t")
+       :to)
+      ((or "list" "v")
+       :list)
+      ;; ((or "tags" "x")
+      ;;  :tags)
+      (_
+       consult-mu-search-sort-field))))
+
+(defun consult-mu--set-mu4e-search-sort-direction (opts)
+  "Dynamically set the `mu4e-search-sort-direction' based on user input.
+
+Uses user input \(i.e. from `consult-mu' command\) to define the sort field.
+
+OPTS is the command line options for mu and can be set by entering options
+in the minibuffer input.  For more details, refer to `consult-grep' and
+consult async documentation.
+
+For example, if the user enters the following in the minibuffer:
+
+“#query -- --maxnum 400 --sortfield from --reverse”
+
+The `mu4e-search-sort-direction' is reversed; If it is set to
+\='ascending, it is toggled to \='descending and vise versa."
+  (if (or (member "-z" opts) (member "--reverse" opts))
+      (pcase consult-mu-search-sort-direction
+        ('descending
+         'ascending)
+        ('ascending
+         'descending))
+    consult-mu-search-sort-direction))
+
+(defun consult-mu--set-mu4e-skip-duplicates (opts)
+  "Dynamically set the `mu4e-search-skip-duplicates' based on user input.
+
+Uses user input \(i.e. from `consult-mu' command\) to define whether to
+skip duplicates.
+
+OPTS is the command line options for mu and can be set by entering options
+in the minibuffer input.  For more details, refer to `consult-grep' and
+consult async documentation.
+
+For example, if the user enters the following in the minibuffer:
+
+“#query -- --maxnum 400 --skip-dups”
+
+The `mu4e-search-skip-duplicates' is set to t."
+  (if (or (member "--skip-dups" opts) mu4e-search-skip-duplicates) t nil))
+
+(defun consult-mu--set-mu4e-results-limit (opts)
+  "Dynamically set the `mu4e-search-results-limit' based on user input.
+
+
+Uses user input \(i.e. from `consult-mu' command\) to define the number of
+results shown.
+
+OPTS is the command line options for mu and can be set by entering options
+in the minibuffer input.  For more details, refer to `consult-grep' and
+consult async documentation.
+
+For example, if the user enters the following in the minibuffer:
+
+“#query -- --maxnum 400”
+
+The `mu4e-search-results-limit' is set to 400."
+  (cond
+   ((member "-n" opts) (string-to-number (nth (+ (cl-position "-n" opts :test 'equal) 1) opts)))
+   ((member "--maxnum" opts) (string-to-number (nth (+ (cl-position "--maxnum" opts :test 'equal) 1) opts)))
+   (t consult-mu-maxnum)))
+
+
+(defun consult-mu--set-mu4e-include-related (opts)
+  "Dynamically set the `mu4e-search-include-related' based on user input.
+
+Uses user input \(i.e. from `consult-mu' command\) to define whether to
+include related messages.
+
+OPTS is the command line options for mu and can be set by entering options
+in the minibuffer input.  For more details, refer to `consult-grep' and
+consult async documentation.
+
+For example if the user enters the following in the minibuffer:
+
+“#query -- --include-related”
+
+The `mu4e-search-include-related' is set to t."
+  (if (or (member "-r" opts) (member "--include-related" opts) mu4e-search-include-related) t nil))
+
+
+
+(defun consult-mu--set-mu4e-threads (opts)
+  "Set  the `mu4e-search-threads' based on `mu4e-search-sort-field'.
+
+Uses user input \(i.e. from `consult-mu' command\) to define whether to
+show threads.
+
+OPTS is the command line options for mu and can be set by entering options
+in the minibuffer input.  For more details, refer to `consult-grep' and
+consult async documentation.
+
+Note that per mu4e docs, when threading is enabled, the headers are
+exclusively sorted by date.  Here the logic is reversed in order to allow
+dynamically sorting by fields other than date \(even when threads are
+enabled\).  In other words, if the sort-field is not the :date, threading
+is disabled because otherwise sort field will be ignored.  This allows the
+user to use command line arguments to sort messages by fields other than
+the date.  For example, the user can enter the following in the minibuffer
+input to sort by subject
+
+“#query -- --sortfield subject”
+
+When the sort-field is :date, the default setting,
+`consult-mu-search-threads' is used, and if that is set to nil, the user
+can use command line arguments \(a.k.a. -t or --thread\) to enable it
+dynamically."
+  (cond
+   ((not (equal mu4e-search-sort-field :date))
+    nil)
+   ((or (member "-t" opts) (member "--threads" opts) consult-mu-search-threads)
+    t)))
+
+(defun consult-mu--update-headers (query ignore-history msg type)
+  "Search for QUERY, and update `consult-mu-headers-buffer-name' buffer.
+
+If IGNORE-HISTORY is true, does *not* update the query history stack,
+`mu4e--search-query-past'.
+If MSG is non-nil, put the cursor on MSG.
+TYPE can be either \=':dynamic or \=':async"
+  (consult-mu--execute-all-marks)
+  (cl-letf* (((symbol-function #'mu4e~headers-append-handler) #'consult-mu--headers-append-handler))
+    (unless (mu4e-running-p) (mu4e--server-start))
+    (let* ((buf (mu4e-get-headers-buffer consult-mu-headers-buffer-name t))
+           (view-buffer (get-buffer consult-mu-view-buffer-name))
+           (expr (car (consult--command-split (substring-no-properties query))))
+           (rewritten-expr (funcall mu4e-query-rewrite-function expr))
+           (mu4e-headers-fields consult-mu-headers-fields))
+      (pcase type
+        (:dynamic)
+        (:async
+         (setq rewritten-expr (funcall mu4e-query-rewrite-function (concat "msgid:" (plist-get msg :message-id)))))
+        (_ ))
+
+      (with-current-buffer buf
+        (save-excursion
+          (let ((inhibit-read-only t))
+            (erase-buffer)
+            (mu4e-headers-mode)
+            (setq-local mu4e-view-buffer-name consult-mu-view-buffer-name)
+            (if view-buffer
+                (setq-local mu4e~headers-view-win (mu4e-display-buffer gnus-article-buffer nil)))
+            (unless ignore-history
+                                        ; save the old present query to the history list
+              (when mu4e--search-last-query
+                (mu4e--search-push-query mu4e--search-last-query 'past)))
+            (setq mu4e--search-last-query rewritten-expr)
+            (setq list-buffers-directory rewritten-expr)
+            (mu4e--modeline-update)
+            (run-hook-with-args 'mu4e-search-hook expr)
+            (consult-mu--headers-clear mu4e~search-message)
+            (setq mu4e~headers-search-start (float-time))
+
+            (pcase-let* ((`(,_arg . ,opts) (consult--command-split query))
+                         (mu4e-search-sort-field (consult-mu--set-mu4e-search-sortfield opts))
+                         (mu4e-search-sort-direction (consult-mu--set-mu4e-search-sort-direction opts))
+                         (mu4e-search-skip-duplicates (consult-mu--set-mu4e-skip-duplicates opts))
+                         (mu4e-search-results-limit (consult-mu--set-mu4e-results-limit opts))
+                         (mu4e-search-threads (consult-mu--set-mu4e-threads opts))
+                         (mu4e-search-include-related (consult-mu--set-mu4e-include-related opts)))
+              (mu4e--server-find
+               rewritten-expr
+               mu4e-search-threads
+               mu4e-search-sort-field
+               mu4e-search-sort-direction
+               mu4e-search-results-limit
+               mu4e-search-skip-duplicates
+               mu4e-search-include-related))
+            (while (or (string-empty-p (buffer-substring (point-min) (point-max)))
+                       (equal (buffer-substring (point-min) (+ (point-min) (length mu4e~search-message))) mu4e~search-message)
+                       (not (or (equal (buffer-substring (- (point-max) (length mu4e~no-matches)) (point-max)) mu4e~no-matches) (equal (buffer-substring (- (point-max) (length mu4e~end-of-results)) (point-max)) mu4e~end-of-results))))
+              (sleep-for 0.005))))))))
+
+(defun consult-mu--execute-all-marks (&optional no-confirmation)
+  "Execute the actions for all marked messages.
+
+Executes all actions for marked messages in the buffer
+`consult-mu-headers-buffer-name'.
+
+If NO-CONFIRMATION is non-nil, don't ask user for confirmation.
+
+This is similar to `mu4e-mark-execute-all' but, with buffer/window
+handling set accordingly for `consult-mu'."
+  (interactive "P")
+  (when-let* ((buf (get-buffer consult-mu-headers-buffer-name)))
+    (with-current-buffer buf
+      (when (eq major-mode 'mu4e-headers-mode)
+        (mu4e--mark-in-context
+         (let* ((marknum (mu4e-mark-marks-num)))
+           (unless (zerop marknum)
+             (pop-to-buffer buf)
+             (unless (one-window-p) (delete-other-windows))
+             (mu4e-mark-execute-all no-confirmation)
+             (quit-window))))))))
+
+(defun consult-mu--headers-goto-message-id (msgid)
+  "Jump to message with MSGID.
+
+This is done in `consult-mu-headers-buffer-name' buffer."
+  (when-let ((buffer consult-mu-headers-buffer-name))
+    (with-current-buffer buffer
+      (setq mu4e-view-buffer-name consult-mu-view-buffer-name)
+      (mu4e-headers-goto-message-id msgid))))
+
+(defun consult-mu--get-message-by-id (msgid)
+  "Find the message with MSGID and return the mu4e MSG plist for it."
+  (cl-letf* (((symbol-function #'mu4e-view) #'consult-mu--view-msg))
+    (when-let ((buffer consult-mu-headers-buffer-name))
+      (with-current-buffer buffer
+        (setq mu4e-view-buffer-name consult-mu-view-buffer-name)
+        (mu4e-headers-goto-message-id msgid)
+        (mu4e-message-at-point)))))
+
+(defun consult-mu--contact-string-to-plist (string)
+  "Convert STRING for contacts to plist.
+
+STRING is the output form mu command, for example from:
+`mu find query --fields f`
+
+Returns a plist with \=':email and \':name keys.
+
+For example
+
+“John Doe <john.doe@example.com>”
+
+will be converted to
+
+\(:name “John Doe” :email “john.doe@example.com”\)"
+  (let* ((string (replace-regexp-in-string ">,\s\\|>;\s" ">\n" string))
+         (list (split-string string "\n" t)))
+    (mapcar (lambda (item)
+              (cond
+               ((string-match "\\(?2:.*\\)\s+<\\(?1:.+\\)>" item)
+                (list :email (or (match-string 1 item) nil) :name (or (match-string 2 item) nil)))
+               ((string-match "^\\(?1:[a-zA-Z0-9\_\.\+\-]+@[a-zA-Z0-9\-]+\.[a-zA-Z0-9\-\.]+\\)" item)
+                (list :email (or (match-string 1 item) nil) :name nil))
+               (t
+                (list :email (format "%s" item) :name nil)))) list)))
+
+(defun consult-mu--contact-name-or-email (contact)
+  "Retrieve name or email of CONTACT.
+
+Looks at the contact plist \(e.g. (:name “John Doe” :email
+“john.doe@example.com”)\) and returns the name.  If the name is missing,
+returns the email address."
+  (cond
+   ((stringp contact)
+    contact)
+   ((listp contact)
+    (mapconcat (lambda (item) (or (plist-get item :name) (plist-get item :email) "")) contact ","))))
+
+(defun consult-mu--headers-template ()
+  "Make headers template using `consult-mu-headers-template'."
+  (if (and consult-mu-headers-template (functionp consult-mu-headers-template))
+      (funcall consult-mu-headers-template)
+    consult-mu-headers-template))
+
+(defun consult-mu--expand-headers-template (msg string)
+  "Expand STRING to create a custom header format for MSG.
+
+See `consult-mu-headers-template' for explanation of the format of
+STRING."
+
+  (cl-loop for c in (split-string string "%" t)
+           concat (concat (pcase  (substring c 0 1)
+                            ("f" (let ((sender (consult-mu--contact-name-or-email (plist-get msg :from)))
+                                       (length (string-to-number (substring c 1 nil))))
+                                   (if sender
+                                       (propertize (if (> length 0) (consult-mu--set-string-width sender length) sender) 'face 'consult-mu-sender-face))))
+                            ("t" (let ((receiver (consult-mu--contact-name-or-email (plist-get msg :to)))
+                                       (length (string-to-number (substring c 1 nil))))
+                                   (if receiver
+                                       (propertize (if (> length 0) (consult-mu--set-string-width receiver length) receiver) 'face 'consult-mu-sender-face))))
+                            ("s" (let ((subject (plist-get msg :subject))
+                                       (length (string-to-number (substring c 1 nil))))
+                                   (if subject
+                                       (propertize (if (> length 0) (consult-mu--set-string-width subject length) subject) 'face 'consult-mu-subject-face))))
+                            ("d" (let ((date (format-time-string "%a %d %b %y" (plist-get msg :date)))
+                                       (length (string-to-number (substring c 1 nil))))
+                                   (if date
+                                       (propertize (if (> length 0) (consult-mu--set-string-width date length) date) 'face 'consult-mu-date-face))))
+
+                            ("p" (let ((priority (plist-get msg :priority))
+                                       (length (string-to-number (substring c 1 nil))))
+                                   (if priority
+                                       (propertize (if (> length 0) (consult-mu--set-string-width (format "%s" priority) length) (format "%s" priority)) 'face 'consult-mu-size-face))))
+                            ("z" (let ((size (file-size-human-readable (plist-get msg :size)))
+                                       (length (string-to-number (substring c 1 nil))))
+                                   (if size
+                                       (propertize (if (> length 0) (consult-mu--set-string-width size length) size)  'face 'consult-mu-size-face))))
+                            ("i" (let ((id (plist-get msg :message-id))
+                                       (length (string-to-number (substring c 1 nil))))
+                                   (if id
+                                       (propertize (if (> length 0) (consult-mu--set-string-width id length) id) 'face 'consult-mu-default-face))))
+
+                            ("g" (let ((flags  (plist-get msg :flags))
+                                       (length (string-to-number (substring c 1 nil))))
+                                   (if flags
+                                       (propertize (if (> length 0) (consult-mu--set-string-width (format "%s" flags) length) (format "%s" flags)) 'face 'consult-mu-flags-face))))
+
+                            ("G" (let ((flags (plist-get msg :flags))
+                                       (length (string-to-number (substring c 1 nil))))
+                                   (if flags
+                                       (propertize (if (> length 0) (consult-mu--set-string-width (format "%s" (mu4e~headers-flags-str flags)) length) (format "%s" (mu4e~headers-flags-str flags))) 'face 'consult-mu-flags-face))))
+
+                            ("x" (let ((tags (plist-get msg :tags))
+                                       (length (string-to-number (substring c 1 nil))))
+                                   (if tags
+                                       (propertize (if (> length 0) (consult-mu--set-string-width tags length) tags) 'face 'consult-mu-tags-face) nil)))
+
+                            ("c" (let ((cc (consult-mu--contact-name-or-email (plist-get msg :cc)))
+                                       (length (string-to-number (substring c 1 nil))))
+                                   (if cc
+                                       (propertize (if (> length 0) (consult-mu--set-string-width cc length) cc) 'face 'consult-mu-tags-face))))
+
+                            ("h" (let ((bcc (consult-mu--contact-name-or-email (plist-get msg :bcc)))
+                                       (length (string-to-number (substring c 1 nil))))
+                                   (if bcc
+                                       (propertize (if (> length 0) (consult-mu--set-string-width bcc length) bcc) 'face 'consult-mu-tags-face))))
+
+                            ("r" (let ((changed (format-time-string "%a %d %b %y" (plist-get msg :changed)))
+                                       (length (string-to-number (substring c 1 nil))))
+                                   (if changed
+                                       (propertize (if (> length 0) (consult-mu--set-string-width changed length) changed) 'face 'consult-mu-tags-face))))
+                            (_ nil))
+                          "  ")))
+
+(defun consult-mu--quit-header-buffer ()
+  "Quits `consult-mu-headers-buffer-name' buffer."
+  (save-mark-and-excursion
+    (when-let* ((buf (get-buffer consult-mu-headers-buffer-name)))
+      (with-current-buffer buf
+        (if (eq major-mode 'mu4e-headers-mode)
+            (mu4e-mark-handle-when-leaving)
+          (quit-window t)
+          ;; clear the decks before going to the main-view
+          (mu4e--query-items-refresh 'reset-baseline))))))
+
+(defun consult-mu--quit-view-buffer ()
+  "Quits `consult-mu-view-buffer-name' buffer."
+  (when-let* ((buf (get-buffer consult-mu-view-buffer-name)))
+    (with-current-buffer buf
+      (if (eq major-mode 'mu4e-view-mode)
+          (mu4e-view-quit)))))
+
+(defun consult-mu--quit-main-buffer ()
+  "Quits `mu4e-main-buffer-name' buffer."
+  (when-let* ((buf (get-buffer mu4e-main-buffer-name)))
+    (with-current-buffer buf
+      (if (eq major-mode 'mu4e-main-mode)
+          (mu4e-quit)))))
+
+(defun consult-mu--lookup ()
+  "Lookup function for `consult-mu' or `consult-mu-async' candidates.
+
+This is passed as LOOKUP to `consult--read' on candidates and is used to
+format the output when a candidate is selected."
+  (lambda (sel cands &rest _args)
+    (let* ((info (cdr (assoc sel cands)))
+           (msg  (plist-get info :msg))
+           (subject (plist-get msg :subject)))
+      (cons subject info))))
+
+(defun consult-mu--group-name (cand)
+  "Get the group name of CAND using `consult-mu-group-by'.
+
+See `consult-mu-group-by' for details of grouping options."
+  (let* ((msg (get-text-property 0 :msg cand))
+         (group (or consult-mu--override-group consult-mu-group-by))
+         (field (if (not (keywordp group)) (intern (concat ":" (format "%s" group))) group)))
+    (pcase field
+      (:date (format-time-string "%a %d %b %y" (plist-get msg field)))
+      (:from (cond
+              ((listp (plist-get msg field))
+               (mapconcat (lambda (item) (or (plist-get item :name) (plist-get item :email))) (plist-get msg field) ";"))
+              ((stringp (plist-get msg field)) (plist-get msg field))))
+      (:to (cond
+            ((listp (plist-get msg field))
+             (mapconcat (lambda (item) (or (plist-get item :name) (plist-get item :email))) (plist-get msg field) ";"))
+            ((stringp (plist-get msg field)) (plist-get msg field))))
+      (:changed (format-time-string "%a %d %b %y" (plist-get msg field)))
+      (:datetime (format-time-string "%F %r" (plist-get msg :date)))
+      (:time (format-time-string "%X" (plist-get msg :date)))
+      (:year (format-time-string "%Y" (plist-get msg :date)))
+      (:month (format-time-string "%B" (plist-get msg :date)))
+      (:day-of-week (format-time-string "%A" (plist-get msg :date)))
+      (:day (format-time-string "%A" (plist-get msg :date)))
+      (:week (format-time-string "%V" (plist-get msg :date)))
+      (:size (file-size-human-readable (plist-get msg field)))
+      (:flags (format "%s" (plist-get msg field)))
+      (:tags (format "%s" (plist-get msg field)))
+      (_ (if (plist-get msg field) (format "%s" (plist-get msg field)) nil)))))
+
+(defun consult-mu--group (cand transform)
+  "Group function for `consult-mu' or `consult-mu-async'.
+
+CAND is passed to `consult-mu--group-name' to get the group for CAND.
+When TRANSFORM is non-nil, the name of CAND is used for group."
+  (when-let ((name (consult-mu--group-name cand)))
+    (if transform (substring cand) name)))
+
+(defun consult-mu--view (msg noselect mark-as-read match-str)
+  "Opens MSG in `consult-mu-headers' and `consult-mu-view'.
+
+If NOSELECT is non-nil, does not select the view buffer/window.
+If MARK-AS-READ is non-nil, marks the MSG as read.
+If MATCH-STR is non-nil, highlights the MATCH-STR in the view buffer."
+  (let ((msgid (plist-get msg :message-id)))
+    (when-let ((buf (mu4e-get-headers-buffer consult-mu-headers-buffer-name t)))
+      (with-current-buffer buf
+        ;;(mu4e-headers-mode)
+        (goto-char (point-min))
+        (setq mu4e-view-buffer-name consult-mu-view-buffer-name)
+        (unless noselect
+          (switch-to-buffer buf))))
+
+    (consult-mu--view-msg msg consult-mu-view-buffer-name)
+
+    (with-current-buffer consult-mu-headers-buffer-name
+      (if msgid
+          (progn
+            (mu4e-headers-goto-message-id msgid)
+            (if mark-as-read
+                (mu4e--server-move (mu4e-message-field-at-point :docid) nil "+S-u-N")))))
+
+    (when match-str
+      (add-to-history 'search-ring match-str)
+      (consult-mu--overlay-match match-str consult-mu-view-buffer-name t))
+
+    (with-current-buffer consult-mu-view-buffer-name
+      (goto-char (point-min)))
+
+    (unless noselect
+      (when msg
+        (select-window (get-buffer-window consult-mu-view-buffer-name))))
+    consult-mu-view-buffer-name))
+
+
+(defun consult-mu--view-action (cand)
+  "Open the candidate, CAND.
+
+This is a wrapper function around `consult-mu--view'.  It parses CAND to
+extract relevant MSG plist and other information and passes them to
+`consult-mu--view'.
+
+To use this as the default action for `consult-mu', set
+`consult-mu-default-action' to \=#'consult-mu--view-action."
+
+  (let* ((info (cdr cand))
+         (msg (plist-get info :msg))
+         (query (plist-get info :query))
+         (match-str (car (consult--command-split query))))
+    (consult-mu--view msg nil consult-mu-mark-viewed-as-read match-str)
+    (consult-mu-overlays-toggle consult-mu-view-buffer-name)))
+
+(defun consult-mu--reply (msg &optional wide-reply)
+  "Reply to MSG using `mu4e-compose-reply'.
+
+If WIDE-REPLY is non-nil use wide-reply \(a.k.a. reply all\) with
+`mu4e-compose-wide-reply'."
+  (let ((msgid (plist-get msg :message-id)))
+    (when-let ((buf (mu4e-get-headers-buffer consult-mu-headers-buffer-name t)))
+      (with-current-buffer buf
+        (goto-char (point-min))
+        (setq mu4e-view-buffer-name consult-mu-view-buffer-name)))
+
+
+    (with-current-buffer consult-mu-headers-buffer-name
+      (mu4e-headers-goto-message-id msgid)
+      (if (not wide-reply)
+          (mu4e-compose-reply)
+        (mu4e-compose-wide-reply)))))
+
+(defun consult-mu--reply-action (cand &optional wide-reply)
+  "Reply to CAND.
+
+This is a wrapper function around `consult-mu--reply'.  It passes
+relevant message plist, from CAND, as well as WIDE-REPLY to
+`consult-mu--reply'.
+
+To use this as the default action for `consult-mu', set
+`consult-mu-default-action' to \=#'consult-mu--reply-action."
+  (let* ((info (cdr cand))
+         (msg (plist-get info :msg))
+         (wide-reply (or wide-reply
+                         (pcase consult-mu-use-wide-reply
+                           ('ask (y-or-n-p "Reply All?"))
+                           ('nil nil)
+                           ('t t)))))
+    (consult-mu--reply msg wide-reply)))
+
+(defun consult-mu--forward (msg)
+  "Forward the MSG using `mu4e-compose-forward'."
+  (let ((msgid (plist-get msg :message-id)))
+    (when-let ((buf (mu4e-get-headers-buffer consult-mu-headers-buffer-name t)))
+      (with-current-buffer buf
+        (goto-char (point-min))
+        (setq mu4e-view-buffer-name consult-mu-view-buffer-name)))
+    (with-current-buffer consult-mu-headers-buffer-name
+      (mu4e-headers-goto-message-id msgid)
+      (mu4e-compose-forward))))
+
+(defun consult-mu--forward-action (cand)
+  "Forward CAND.
+
+This is a wrapper function around `consult-mu--forward'.  It passes
+the relevant message plist, from CAND to `consult-mu--forward'.
+
+To use this as the default action for `consult-mu', set
+`consult-mu-default-action' to \=#'consult-mu--forward-action."
+  (let* ((info (cdr cand))
+         (msg (plist-get info :msg)))
+    (consult-mu--forward msg)))
+
+(defun consult-mu--get-split-style-character (&optional style)
+  "Get the character for consult async split STYLE.
+
+STYLE defaults to `consult-async-split-style'."
+  (let ((style (or style consult-async-split-style 'none)))
+    (or (char-to-string (plist-get (alist-get style consult-async-split-styles-alist) :initial))
+        (char-to-string (plist-get (alist-get style consult-async-split-styles-alist) :separator))
+        "")))
+
+(defun consult-mu--dynamic-format-candidate (cand highlight)
+  "Format minibuffer candidate, CAND.
+
+CAND is the minibuffer completion candidate \(a mu4e message collected by
+`consult-mu--dynamic-collection'\).  If HIGHLIGHT is non-nil, it is
+highlighted with `consult-mu-highlight-match-face'."
+
+  (let* ((string (car cand))
+         (info (cadr cand))
+         (msg (plist-get info :msg))
+         (query (plist-get info :query))
+         (match-str (if (stringp query) (consult--split-escaped (car (consult--command-split query))) nil))
+         (headers-template (consult-mu--headers-template))
+         (str (if headers-template
+                  (consult-mu--expand-headers-template msg headers-template)
+                string))
+         (str (propertize str :msg msg :query query :type :dynamic)))
+    (if (and consult-mu-highlight-matches highlight)
+        (cond
+         ((listp match-str)
+          (mapc (lambda (match) (setq str (consult-mu--highlight-match match str t))) match-str))
+         ((stringp match-str)
+          (setq str (consult-mu--highlight-match match-str str t))))
+      str)
+    (when msg
+      (cons str (list :msg msg :query query :type :dynamic)))))
+
+(defun consult-mu--dynamic-collection (input)
+  "Dynamically collect mu4e search results.
+
+INPUT is the user input.  It is passed as QUERY to
+`consult-mu--update-headers', appends the result to
+`consult-mu-headers-buffer-name' and returns a list of found
+messages."
+
+  (save-excursion
+    (pcase-let* ((`(,_arg . ,opts) (consult--command-split input)))
+      (consult-mu--update-headers (substring-no-properties input) nil nil :dynamic)
+      (if (or (member "-g" opts)  (member "--group" opts))
+          (cond
+           ((member "-g" opts)
+            (setq consult-mu--override-group (intern (or (nth (+ (cl-position "-g" opts :test 'equal) 1) opts) "nil"))))
+           ((member "--group" opts)
+            (setq consult-mu--override-group (intern (or (nth (+ (cl-position "--group" opts :test 'equal) 1) opts) "nil")))))
+        (setq consult-mu--override-group nil)))
+
+    (with-current-buffer consult-mu-headers-buffer-name
+      (goto-char (point-min))
+      (remove nil
+              (cl-loop until (eobp)
+                       collect (consult-mu--dynamic-format-candidate (list (buffer-substring (point) (line-end-position)) (list :msg (ignore-errors (mu4e-message-at-point)) :query input)) t)
+                       do (forward-line 1))))))
+
+(defun consult-mu--dynamic-state ()
+  "State function for `consult-mu' candidates.
+This is passed as STATE to `consult--read' and is used to preview or do
+other actions on the candidate."
+  (lambda (action cand)
+    (let ((preview (consult--buffer-preview)))
+      (pcase action
+        ('preview
+         (if cand
+             (when-let* ((info (cdr cand))
+                         (msg (plist-get info :msg))
+                         (query (plist-get info :query))
+                         (msgid (substring-no-properties (plist-get msg :message-id)))
+                         (match-str (car (consult--command-split query)))
+                         (match-str (car (consult--command-split query)))
+                         (mu4e-headers-buffer-name consult-mu-headers-buffer-name)
+                         (buffer consult-mu-view-buffer-name))
+               ;;(get-buffer-create consult-mu-view-buffer-name)
+               (add-to-list 'consult-mu--view-buffers-list buffer)
+               (funcall preview action
+                        (consult-mu--view msg t consult-mu-mark-previewed-as-read match-str))
+               (with-current-buffer consult-mu-view-buffer-name
+                 (unless (one-window-p) (delete-other-windows))))))
+        ('return
+         (save-mark-and-excursion
+           (consult-mu--execute-all-marks))
+         (setq consult-mu--override-group nil)
+         cand)))))
+
+(defun consult-mu--dynamic (prompt collection &optional initial)
+  "Query mu4e messages dyunamically.
+
+This is a non-interactive internal function.  For the interactive version
+see `consult-mu'.
+
+It runs the `consult-mu--dynamic-collection' to do a `mu4e-search' with
+user input \(e.g. INITIAL\) and returns the results \(list of messages
+found\) as a completion table in minibuffer.
+
+The completion table gets dynamically updated as the user types in the
+minibuffer.  Each candidate in the minibuffer is formatted by
+`consult-mu--dynamic-format-candidate' to add annotation and other info to
+the candidate.
+
+Description of Arguments:
+  PROMPT     the prompt in the minibuffer
+             \(passed as PROMPT to   `consult--read'\)
+  COLLECTION a colection function passed to `consult--dynamic-collection'.
+  INITIAL    an optional arg for the initial input in the minibuffer.
+             \(passed as INITITAL to `consult--read'\)
+
+commandline arguments/options \(see `mu find --help` in the command line
+for details\) can be passed to the minibuffer input similar to
+`consult-grep'.  For example the user can enter:
+
+“#paper -- --maxnum 200 --sortfield from --reverse”
+
+this will search for mu4e messages with the query “paper”, retrives a
+maximum of 200 messages and sorts them by the “from:” field and reverses
+the sort direction (opposite of `consult-mu-search-sort-field').
+
+Note that some command line arguments are not supported by mu4e (for
+example sorting based on cc: or bcc: fields are not supported in
+`mu4e-search-sort-field')
+
+Also, the results can further be narrowed by
+`consult-async-split-style' \(e.g. by entering “#” when
+`consult-async-split-style' is set to \='perl\).
+
+For example:
+
+“#paper -- --maxnum 200 --sortfield from --reverse#accepted”
+
+will retrieve the message as the example above, then narrows down the
+candidates to those that  that match “accepted”."
+  (consult--read
+   (consult--dynamic-collection (or collection #'consult-mu--dynamic-collection))
+   :prompt (or prompt "Select: ")
+   :lookup (consult-mu--lookup)
+   :state (funcall #'consult-mu--dynamic-state)
+   :initial initial
+   :group #'consult-mu--group
+   :add-history (append (list (thing-at-point 'symbol))
+                        consult-mu-saved-searches-dynamic)
+   :history '(:input consult-mu--history)
+   :require-match t
+   :category 'consult-mu-messages
+   :preview-key consult-mu-preview-key
+   :sort nil))
+
+(defun consult-mu-dynamic (&optional initial noaction)
+  "Lists results of `mu4e-search' dynamically.
+
+This is an interactive wrapper function around `consult-mu--dynamic'.  It
+queries the user for a search term in the minibuffer, then fetches a list
+of messages for the entered search term as a minibuffer completion table
+for selection.  The list of candidates in the completion table are
+dynamically updated as the user changes the entry.
+
+Upon selection of a candidate either
+ - the candidate is returned if NOACTION is non-nil
+ or
+ - the candidate is passed to `consult-mu-action' if NOACTION is nil.
+
+Additional commandline arguments can be passed in the minibuffer entry by
+typing “--” followed by command line arguments.
+
+For example, the user can enter:
+
+“#consult-mu -- -n 10”
+
+this will run a `mu4e-search' with the query “consult-mu” and changes the
+search limit \(i.e. `mu4e-search-results-limit' to 10\).
+
+
+Also, the results can further be narrowed by
+`consult-async-split-style' \(e.g. by entering “#” when
+`consult-async-split-style' is set to \='perl\).
+
+For example:
+
+“#consult-mu -- -n 10#github”
+
+will retrieve the messages as the example above, then narrows down the
+completion table to candidates that match “github”.
+
+INITIAL is an optional arg for the initial input in the minibuffer.
+\(passed as INITITAL to `consult-mu--dynamic'\)
+
+For more details on consult--async functionalities, see `consult-grep' and
+the official manual of consult, here:
+URL `https://github.com/minad/consult'"
+  (interactive)
+  (save-mark-and-excursion
+    (consult-mu--execute-all-marks))
+  (let* ((sel
+          (consult-mu--dynamic (concat "[" (propertize "consult-mu-dynamic" 'face 'consult-mu-sender-face) "]" " Search For:  ") #'consult-mu--dynamic-collection initial)))
+    (save-mark-and-excursion
+      (consult-mu--execute-all-marks))
+    (if noaction
+        sel
+      (progn
+        (funcall consult-mu-action sel)
+        sel))))
+
+(defun consult-mu--async-format-candidate (string input highlight)
+  "Formats minibuffer candidates for `consult-mu-async'.
+
+STRING is the output retrieved from `mu find INPUT ...` in the command line.
+INPUT is the query from the user.
+
+If HIGHLIGHT is t, input is highlighted with
+`consult-mu-highlight-match-face' in the minibuffer."
+
+  (let* ((query input)
+         (parts (split-string (replace-regexp-in-string "^\\\\->\s\\|^\\\/->\s" "" string) consult-mu-delimiter))
+         (msgid (car parts))
+         (date (date-to-time (cadr parts)))
+         (sender (cadr (cdr parts)))
+         (sender (consult-mu--contact-string-to-plist sender))
+         (receiver (cadr (cdr (cdr parts))))
+         (receiver (consult-mu--contact-string-to-plist receiver))
+         (subject (cadr (cdr (cdr (cdr parts)))))
+         (size (string-to-number (cadr (cdr (cdr (cdr (cdr parts)))))))
+         (flags (consult-mu-flags-to-string (cadr (cdr (cdr (cdr (cdr (cdr parts))))))))
+         (tags (cadr (cdr (cdr (cdr (cdr (cdr (cdr parts))))))))
+         (priority (cadr (cdr (cdr (cdr (cdr (cdr (cdr (cdr parts)))))))))
+         (cc (cadr (cdr (cdr (cdr (cdr (cdr (cdr (cdr (cdr parts))))))))))
+         (cc (consult-mu--contact-string-to-plist cc))
+         (bcc (cadr (cdr (cdr (cdr (cdr (cdr (cdr (cdr (cdr (cdr parts)))))))))))
+         (bcc (consult-mu--contact-string-to-plist bcc))
+         (path (cadr (cdr (cdr (cdr (cdr (cdr (cdr (cdr (cdr (cdr (cdr parts))))))))))))
+         (msg (list :subject subject :date date :from sender :to receiver :size size :message-id msgid :flags flags :tags tags :priority priority :cc cc :bcc bcc :path path))
+         (match-str (if (stringp input) (consult--split-escaped (car (consult--command-split query))) nil))
+         (headers-template (consult-mu--headers-template))
+         (str (if headers-template
+                  (consult-mu--expand-headers-template msg headers-template)
+                (format "%s\s\s%s\s\s%s\s\s%s\s\s%s\s\s%s"
+                        (propertize (consult-mu--set-string-width
+                                     (format-time-string "%x" date) 10)
+                                    'face 'consult-mu-date-face)
+                        (propertize (consult-mu--set-string-width (consult-mu--contact-name-or-email sender) (floor (* (frame-width) 0.2)))  'face 'consult-mu-sender-face)
+                        (propertize (consult-mu--set-string-width subject (floor (* (frame-width) 0.55))) 'face 'consult-mu-subject-face)
+                        (propertize (file-size-human-readable size) 'face 'consult-mu-size-face)
+                        (propertize (format "%s" flags) 'face 'consult-mu-flags-face)
+                        (propertize (if tags (format "%s" tags) nil) 'face 'consult-mu-tags-face))))
+         (str (propertize str :msg msg :query query :type :async)))
+    (if (and consult-mu-highlight-matches highlight)
+        (cond
+         ((listp match-str)
+          (mapc (lambda (match) (setq str (consult-mu--highlight-match match str t))) match-str))
+         ((stringp match-str)
+          (setq str (consult-mu--highlight-match match-str str t))))
+      str)
+    (cons str (list :msg msg :query query :type :async))))
+
+(defun consult-mu--async-state ()
+  "State function for `consult-mu-async' candidates.
+
+This is passed as STATE to `consult--read' and is used to preview or do
+other actions on the candidate."
+  (lambda (action cand)
+    (let ((preview (consult--buffer-preview)))
+      (pcase action
+        ('preview
+         (if cand
+             (when-let* ((info (cdr cand))
+                         (msg (plist-get info :msg))
+                         (msgid (substring-no-properties (plist-get msg :message-id)))
+                         (query (plist-get info :query))
+                         (match-str (car (consult--command-split query)))
+                         (mu4e-headers-buffer-name consult-mu-headers-buffer-name)
+                         (buffer consult-mu-view-buffer-name))
+               (add-to-list 'consult-mu--view-buffers-list buffer)
+               (funcall preview action
+                        (consult-mu--view msg t consult-mu-mark-previewed-as-read match-str))
+               (with-current-buffer consult-mu-view-buffer-name
+                 (unless (one-window-p) (delete-other-windows))))))
+        ('return
+         (save-mark-and-excursion
+           (consult-mu--execute-all-marks))
+         cand)))))
+
+(defun consult-mu--async-transform (input)
+  "Add annotation to minibuffer candiates for `consult-mu'.
+
+Format each candidates with `consult-gh--repo-format' and INPUT."
+  (lambda (cands)
+    (cl-loop for cand in cands
+             collect
+             (consult-mu--async-format-candidate cand input t))))
+
+(defun consult-mu--async-builder (input)
+  "Build mu command line for searching messages by INPUT (e.g. `mu find INPUT)`."
+  (pcase-let* ((consult-mu-args (append consult-mu-args '("find")))
+               (cmd (consult--build-args consult-mu-args))
+               (`(,arg . ,opts) (consult--command-split input))
+               (flags (append cmd opts))
+               (sortfield (cond
+                           ((member "-s" flags) (nth (+ (cl-position "-s" opts :test 'equal) 1) flags))
+                           ((member "--sortfield" flags) (nth (+ (cl-position "--sortfield" flags :test 'equal) 1) flags))
+                           (t (substring (symbol-name consult-mu-search-sort-field) 1))))
+               (threads (if (not (equal sortfield :date)) nil (or (member "-t" flags) (member "--threads" flags) mu4e-search-threads)))
+               (skip-dups (or (member "-u" flags) (member "--skip-dups" flags) mu4e-search-skip-duplicates))
+               (include-related (or (member "-r" flags) (member "--include-related" flags) mu4e-search-include-related)))
+    (if (or (member "-g" flags)  (member "--group" flags))
+        (cond
+         ((member "-g" flags)
+          (setq consult-mu--override-group (intern (or (nth (+ (cl-position "-g" opts :test 'equal) 1) opts) "nil")))
+          (setq opts (remove "-g" (remove (nth (+ (cl-position "-g" opts :test 'equal) 1) opts) opts))))
+         ((member "--group" flags)
+          (setq consult-mu--override-group (intern (or (nth (+ (cl-position "--group" opts :test 'equal) 1) opts) "nil")))
+          (setq opts (remove "--group" (remove (nth (+ (cl-position "--group" opts :test 'equal) 1) opts) opts)))))
+      (setq consult-mu--override-group nil))
+    (setq opts (append opts (list "--nocolor")))
+    (setq opts (append opts (list "--fields" (format "i%sd%sf%st%ss%sz%sg%sx%sp%sc%sh%sl"
+                                                     consult-mu-delimiter consult-mu-delimiter consult-mu-delimiter consult-mu-delimiter consult-mu-delimiter consult-mu-delimiter consult-mu-delimiter consult-mu-delimiter consult-mu-delimiter consult-mu-delimiter consult-mu-delimiter))))
+    (unless (or (member "-s" flags) (member "--sortfiled" flags))
+      (setq opts (append opts (list "--sortfield" (substring (symbol-name consult-mu-search-sort-field) 1)))))
+    (if threads (setq opts (append opts (list "--thread"))))
+    (if skip-dups (setq opts (append opts (list "--skip-dups"))))
+    (if include-related (setq opts (append opts (list "--include-related"))))
+    (cond
+     ((and (member "-n" flags) (< (string-to-number (nth (+ (cl-position "-n" opts :test 'equal) 1) opts)) 0))
+      (setq opts (remove "-n" (remove (nth (+ (cl-position "-n" opts :test 'equal) 1) opts) opts))))
+     ((and (member "--maxnum" flags) (< (string-to-number (nth (+ (cl-position "--maxnum" opts :test 'equal) 1) opts)) 0))
+      (setq opts (remove "--maxnum" (remove (nth (+ (cl-position "--maxnum" opts :test 'equal) 1) opts) opts)))))
+    (unless (or (member "-n" flags)  (member "--maxnum" flags))
+      (if (and consult-mu-maxnum (> consult-mu-maxnum 0))
+          (setq opts (append opts (list "--maxnum" (format "%s" consult-mu-maxnum))))))
+
+    (pcase consult-mu-search-sort-direction
+      ('descending
+       (if (or (member "-z" flags) (member "--reverse" flags))
+           (setq opts (remove "-z" (remove "--reverse" opts)))
+         (setq opts (append opts (list "--reverse")))))
+      ('ascending)
+      (_))
+    (pcase-let* ((`(,re . ,hl) (funcall consult--regexp-compiler arg 'basic t)))
+      (when re
+        (cons (append cmd
+                      (list (string-join re " "))
+                      opts)
+              hl)))))
+
+(defun consult-mu--async (prompt builder &optional initial)
+  "Query mu4e messages asynchronously.
+
+This is a non-interactive internal function.  For the interactive
+version, see `consult-mu-async'.
+
+It runs the command line from `consult-mu--async-builder' in an async
+process and returns the results (list of messages) as a completion table
+in minibuffer that will be passed to `consult--read'.  The completion
+table gets dynamically updated as the user types in the minibuffer.  Each
+candidate in the minibuffer is formatted by `consult-mu--async-transform'
+to add annotation and other info to the candidate.
+
+Description of Arguments:
+
+PROMPT  the prompt in the minibuffer
+        \(passed as PROMPT to `consult--red'\)
+BUILDER an async builder function passed to `consult--async-command'
+INITIAL an optional arg for the initial input in the minibuffer
+        \(passed as INITITAL to `consult--read'\)
+
+commandline arguments/options \(see `mu find --help` in the command line
+for details\) can be passed to the minibuffer input similar to
+`consult-grep'.  For example the user can enter:
+
+“#paper -- --maxnum 200 --sortfield from --reverse”
+
+this will search for mu4e messages with the query “paper”, retrives a
+maximum of 200 messages sorts them by the “from:” field and reverses the
+sort direction (opposite of `consult-mu-search-sort-field').
+
+Also, the results can further be narrowed by
+`consult-async-split-style' \(e.g. by entering “#” when
+`consult-async-split-style' is set to \='perl\).
+
+For example:
+
+`#paper -- --maxnum 200 --sortfield from --reverse#accepted'
+
+will retrieve the message as the example above, then narrows down the
+completion table to candidates that match “accepted”."
+  (consult--read
+   (consult--process-collection builder
+     :transform (consult--async-transform-by-input #'consult-mu--async-transform))
+   :prompt prompt
+   :lookup (consult-mu--lookup)
+   :state (funcall #'consult-mu--async-state)
+   :initial initial
+   :group #'consult-mu--group
+   :add-history (append (list (thing-at-point 'symbol))
+                        consult-mu-saved-searches-async)
+   :history '(:input consult-mu--history)
+   :require-match t
+   :category 'consult-mu-messages
+   :preview-key consult-mu-preview-key
+   :sort nil))
+
+(defun consult-mu-async (&optional initial noaction)
+  "Lists results of `mu find` Asynchronously.
+
+This is an interactive wrapper function around `consult-mu--async'.  It
+queries the user for a search term in the minibuffer, then fetches a list
+of messages for the entered search term as a minibuffer completion table
+for selection.  The list of candidates in the completion table are
+dynamically updated as the user changes the entry.
+
+Upon selection of a candidate either
+ - the candidate is returned if NOACTION is non-nil
+ or
+ - the candidate is passed to `consult-mu-action' if NOACTION is nil.
+
+Additional commandline arguments can be passed in the minibuffer entry by
+typing `--` followed by command line arguments.
+
+For example the user can enter:
+
+`#consult-mu -- -n 10'
+
+this will run a `mu4e-search' with the query \"consult-my\" and changes the
+search limit (i.e. `mu4e-search-results-limit' to 10.
+
+
+Also, the results can further be narrowed by `consult-async-split-style'
+\(e.g. by entering “#” when `consult-async-split-style' is set to \='perl\).
+
+For example:
+
+“#consult-mu -- -n 10#github”
+
+will retrieve the message as the example above, then narrows down the
+completion table to candidates that match “github”.
+
+INITIAL is an optional arg for the initial input in the minibuffer.
+\(passed as INITITAL to `consult-mu--async'\).
+
+For more details on consult--async functionalities, see `consult-grep' and
+the official manual of consult, here:
+URL `https://github.com/minad/consult'
+
+Note that this is the async search directly using the commandline `mu`
+command and not mu4e-search. As a result, mu4e-headers buffers are not
+created until a single message is selected \(or interacted with using
+embark, etc.\)  Previews are shown in a mu4e-view buffer \(see
+`consult-mu-view-buffer-name'\) attached to an empty mu4e-headers buffer
+\(i.e. `consult-mu-headers-buffer-name'\).  This allows quick retrieval of
+many messages \(tens of thousands\) and previews, but not opening the
+results in a mu4e-headers buffer.  If you want ot open the results in a
+mu4e-headers buffer for other work flow, then you should use the
+dynamically collected function `consult-mu' which is slower if searching
+for many emails but allows follow up interactions in a mu4e-headers
+buffer."
+  (interactive)
+  (save-mark-and-excursion
+    (consult-mu--execute-all-marks))
+  (let* ((sel
+          (consult-mu--async (concat "[" (propertize "consult-mu async" 'face 'consult-mu-sender-face) "]" " Search For:  ") #'consult-mu--async-builder initial))
+         (info (cdr sel))
+         (msg (plist-get info :msg))
+         (query (plist-get info :query)))
+    (save-mark-and-excursion
+      (consult-mu--execute-all-marks))
+    (if noaction
+        sel
+      (progn
+        (consult-mu--update-headers query t msg :async))
+      (funcall consult-mu-action sel)
+      sel)))
+
+(defun consult-mu (&optional initial noaction)
+  "Default interactive command.
+
+This is a wrapper function that calls `consult-mu-default-command' with
+INITIAL and NOACTION.
+
+For example, the `consult-mu-default-command can be set to
+ `#'consult-mu-dynamic' sets the default behavior to dynamic collection
+ `#'consult-mu-async' sets the default behavior to async collection"
+
+  (interactive "P")
+  (funcall consult-mu-default-command initial noaction))
+
+;;; provide `consult-mu' module
+(provide 'consult-mu)
+
+;;; consult-mu.el ends here