Commit 453eb1013d6f
Changed files (4)
tools
emacs
tools/emacs/lisp/consult-mu
@@ -0,0 +1,1 @@
+Subproject commit e1dc63674b924698b30a9ecc0400a05864711c85
tools/emacs/lisp/consult-mu-embark.el
@@ -1,249 +0,0 @@
-;;; 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
@@ -1,1673 +0,0 @@
-;;; 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
.gitmodules
@@ -1,3 +1,6 @@
[submodule "tools/emacs/lisp/aider.el"]
path = tools/emacs/lisp/aider.el
url = https://github.com/tninja/aider.el
+[submodule "tools/emacs/lisp/consult-mu"]
+ path = tools/emacs/lisp/consult-mu
+ url = https://github.com/armindarvish/consult-mu