nftable-migration
1;;; consult-mu-embark.el --- Emabrk Actions for consult-mu -*- lexical-binding: t -*-
2
3;; Copyright (C) 2021-2023
4
5;; Author: Armin Darvish
6;; Maintainer: Armin Darvish
7;; Created: 2023
8;; Version: 1.0
9;; Package-Requires: ((emacs "28.0") (consult "2.0"))
10;; Homepage: https://github.com/armindarvish/consult-mu
11;; Keywords: convenience, matching, tools, email
12;; Homepage: https://github.com/armindarvish/consult-mu
13
14;; SPDX-License-Identifier: GPL-3.0-or-later
15
16;; This file is free software: you can redistribute it and/or modify
17;; it under the terms of the GNU General Public License as published
18;; by the Free Software Foundation, either version 3 of the License,
19;; or (at your option) any later version.
20;;
21;; This file is distributed in the hope that it will be useful,
22;; but WITHOUT ANY WARRANTY; without even the implied warranty of
23;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24;; GNU General Public License for more details.
25;;
26;; You should have received a copy of the GNU General Public License
27;; along with this file. If not, see <https://www.gnu.org/licenses/>.
28
29
30;;; Commentary:
31
32;; This package provides an alternative interactive serach interface for
33;; mu and mu4e (see URL `https://djcbsoftware.nl/code/mu/mu4e.html').
34;; It uses a consult-based minibuffer completion for searching and
35;; selecting, and marking emails, as well as additional utilities for
36;; composing emails and more.
37
38;; This package requires mu4e version "1.10.8" or later.
39
40;;; Code:
41
42;;; Requirements
43(require 'embark)
44(require 'consult-mu)
45
46;;; Customization Variables
47(defcustom consult-mu-embark-noconfirm-before-execute nil
48 "Should consult-mu-embark skip confirmation when executing marks?"
49 :group 'consult-mu
50 :type 'boolean)
51
52;;; Define Embark Action Functions
53(defun consult-mu-embark-default-action (cand)
54 "Run `consult-mu-action' on the candidate, CAND."
55 (let* ((msg (get-text-property 0 :msg cand))
56 (query (get-text-property 0 :query cand))
57 (type (get-text-property 0 :type cand))
58 (newcand (cons cand `(:msg ,msg :query ,query :type ,type))))
59 (if (equal type :async)
60 (consult-mu--update-headers query t msg :async))
61 (funcall consult-mu-action newcand)))
62
63
64
65(defun consult-mu-embark-reply (cand)
66 "Reply to message in CAND."
67 (let* ((msg (get-text-property 0 :msg cand))
68 (query (get-text-property 0 :query cand))
69 (type (get-text-property 0 :type cand)))
70 (if (equal type :async)
71 (consult-mu--update-headers query t msg :async))
72 (consult-mu--reply msg nil)))
73
74(defun consult-mu-embark-wide-reply (cand)
75 "Reply all for message in CAND."
76 (let* ((msg (get-text-property 0 :msg cand))
77 (query (get-text-property 0 :query cand))
78 (type (get-text-property 0 :type cand)))
79 (if (equal type :async)
80 (consult-mu--update-headers query t msg :async))
81 (consult-mu--reply msg )))
82
83(defun consult-mu-embark-forward (cand)
84 "Forward the message in CAND."
85 (let* ((msg (get-text-property 0 :msg cand))
86 (query (get-text-property 0 :query cand))
87 (type (get-text-property 0 :type cand)))
88 (if (equal type :async)
89 (consult-mu--update-headers query t msg :async))
90 (consult-mu--forward msg)))
91
92(defun consult-mu-embark-kill-message-field (cand)
93 "Get a header field of message in CAND."
94 (let* ((msg (get-text-property 0 :msg cand))
95 (query (get-text-property 0 :query cand))
96 (type (get-text-property 0 :type cand))
97 (msg-id (plist-get msg :message-id)))
98 (if (equal type :async)
99 (consult-mu--update-headers query t msg :async))
100 (with-current-buffer consult-mu-headers-buffer-name
101 (unless (equal (mu4e-message-field-at-point :message-id) msg-id)
102 (mu4e-headers-goto-message-id msg-id))
103 (if (equal (mu4e-message-field-at-point :message-id) msg-id)
104 (progn
105 (mu4e~headers-update-handler msg nil nil))))
106
107 (with-current-buffer consult-mu-view-buffer-name
108 (kill-new (consult-mu--message-get-header-field))
109 (consult-mu--pulse-region (point) (line-end-position)))))
110
111(defun consult-mu-embark-save-attachmnts (cand)
112 "Save attachments of CAND."
113 (let* ((msg (get-text-property 0 :msg cand))
114 (query (get-text-property 0 :query cand))
115 (type (get-text-property 0 :type cand))
116 (msg-id (plist-get msg :message-id)))
117
118 (if (equal type :async)
119 (consult-mu--update-headers query t msg :async))
120
121 (with-current-buffer consult-mu-headers-buffer-name
122 (unless (equal (mu4e-message-field-at-point :message-id) msg-id)
123 (mu4e-headers-goto-message-id msg-id))
124 (if (equal (mu4e-message-field-at-point :message-id) msg-id)
125 (progn
126 (mu4e~headers-update-handler msg nil nil))))
127
128 (with-current-buffer consult-mu-view-buffer-name
129 (goto-char (point-min))
130 (re-search-forward "^\\(Attachment\\|Attachments\\): " nil t)
131 (consult-mu--pulse-region (point) (line-end-position))
132 (mu4e-view-save-attachments t))))
133
134(defun consult-mu-embark-search-messages-from-contact (cand)
135 "Search messages from the same sender as the message in CAND."
136 (let* ((msg (get-text-property 0 :msg cand))
137 (from (car (plist-get msg :from)))
138 (email (plist-get from :email)))
139 (consult-mu (concat "from:" email))))
140
141(defun consult-mu-embark-search-messages-with-subject (cand)
142 "Search all messages for the same subject as the message in CAND."
143 (let* ((msg (get-text-property 0 :msg cand))
144 ;;(subject (replace-regexp-in-string ":\\|#\\|\\.\\|\\+" "" (plist-get msg :subject)))
145 (subject (replace-regexp-in-string ":\\|#\\|\\.\\|\\+\\|\\(\\[.*\\]\\)" "" (format "%s" (plist-get msg :subject)))))
146 (consult-mu (concat "subject:" subject))))
147
148;; macro for defining functions for marks
149(defmacro consult-mu-embark--defun-mark-for (mark)
150 "Define a function mu4e-view-mark-for- MARK."
151 (let ((funcname (intern (format "consult-mu-embark-mark-for-%s" mark)))
152 (docstring (format "Mark the current message for %s." mark)))
153 `(progn
154 (defun ,funcname (cand) ,docstring
155 (let* ((msg (get-text-property 0 :msg cand))
156 (msgid (plist-get msg :message-id))
157 (query (get-text-property 0 :query cand))
158 (buf (get-buffer consult-mu-headers-buffer-name)))
159 (if buf
160 (progn
161 (with-current-buffer buf
162 (if (eq major-mode 'mu4e-headers-mode)
163 (progn
164 (goto-char (point-min))
165 (mu4e-headers-goto-message-id msgid)
166 (if (equal (mu4e-message-field-at-point :message-id) msgid)
167 (mu4e-headers-mark-and-next ',mark)
168 (progn
169 (consult-mu--update-headers query t msg :async)
170 (with-current-buffer buf
171 (goto-char (point-min))
172 (mu4e-headers-goto-message-id msgid)
173 (if (equal (mu4e-message-field-at-point :message-id) msgid)
174 (mu4e-headers-mark-and-next ',mark))))))
175 (progn
176 (consult-mu--update-headers query t msg :async)
177 (with-current-buffer buf
178 (goto-char (point-min))
179 (mu4e-headers-goto-message-id msgid)
180 (if (equal (mu4e-message-field-at-point :message-id) msgid)
181 (mu4e-headers-mark-and-next ',mark)))))))))))))
182
183;; add embark functions for marks
184(defun consult-mu-embark--defun-func-for-marks (marks)
185 "Run the macro `consult-mu-embark--defun-mark-for' on MARKS.
186
187MARKS is a list of marks.
188
189This is useful for creating embark functions for all the `mu4e-marks'
190elements."
191 (mapcar (lambda (mark) (eval `(consult-mu-embark--defun-mark-for ,mark))) marks))
192
193;; use consult-mu-embark--defun-func-for-marks to make a function for each `mu4e-marks' element.
194(consult-mu-embark--defun-func-for-marks (mapcar 'car mu4e-marks))
195
196;;; Define Embark Keymaps
197(defvar-keymap consult-mu-embark-general-actions-map
198 :doc "Keymap for consult-mu-embark"
199 :parent embark-general-map)
200
201(add-to-list 'embark-keymap-alist '(consult-mu . consult-mu-embark-general-actions-map))
202
203
204(defvar-keymap consult-mu-embark-messages-actions-map
205 :doc "Keymap for consult-mu-embark-messages"
206 :parent consult-mu-embark-general-actions-map
207 "r" #'consult-mu-embark-reply
208 "w" #'consult-mu-embark-wide-reply
209 "f" #'consult-mu-embark-forward
210 "?" #'consult-mu-embark-kill-message-field
211 "c" #'consult-mu-embark-search-messages-from-contact
212 "s" #'consult-mu-embark-search-messages-with-subject
213 "S" #'consult-mu-embark-save-attachmnts)
214
215(add-to-list 'embark-keymap-alist '(consult-mu-messages . consult-mu-embark-messages-actions-map))
216
217
218;; add mark keys to `consult-mu-embark-messages-actions-map' keymap
219(defun consult-mu-embark--add-keys-for-marks (marks)
220 "Add a key for each mark in MARKS to embark map.
221
222Adds the keys in `consult-mu-embark-messages-actions-map', and binds the
223combination “m key”, where key is the :char in mark plist in the
224`consult-mu-embark-messages-actions-map' to the function defined by the
225prefix “consult-mu-embark-mark-for-” and mark.
226
227This is useful for adding all `mu4e-marks' to embark key bindings under a
228submenu (called by “m”), for example, the default mark-for-archive mark
229that is bound to r in mu4e buffers can be called in embark by “m r”."
230 (mapcar (lambda (mark)
231 (let* ((key (plist-get (cdr mark) :char))
232 (key (cond ((consp key) (car key)) ((stringp key) key)))
233 (func (intern (concat "consult-mu-embark-mark-for-" (format "%s" (car mark)))))
234 (key (concat "m" key)))
235 (define-key consult-mu-embark-messages-actions-map key func)))
236 marks))
237
238;; add all `mu4e-marks to embark keybindings. See `consult-mu-embark--add-keys-for-marks' above for more details
239(consult-mu-embark--add-keys-for-marks mu4e-marks)
240
241;; change the default action on `consult-mu-messages' category.
242(add-to-list 'embark-default-action-overrides '(consult-mu-messages . consult-mu-embark-default-action))
243
244
245;;; Provide `consult-mu-embark' module
246
247(provide 'consult-mu-embark)
248
249;;; consult-mu-embark.el ends here