2023-09-20 21:31:19 +02:00
|
|
|
|
;;; mu4e-mark.el --- Marking messages -*- lexical-binding: t -*-
|
2020-02-11 12:23:40 +01:00
|
|
|
|
|
2024-04-09 18:55:03 +02:00
|
|
|
|
;; Copyright (C) 2011-2024 Dirk-Jan C. Binnema
|
2012-04-23 18:07:20 +02:00
|
|
|
|
|
|
|
|
|
;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
|
|
|
|
;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
|
|
|
|
|
|
|
|
|
;; This file is not part of GNU Emacs.
|
2020-02-11 12:23:40 +01:00
|
|
|
|
|
2021-05-29 23:36:13 +02:00
|
|
|
|
;; mu4e is free software: you can redistribute it and/or modify
|
2012-04-23 18:07:20 +02:00
|
|
|
|
;; 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.
|
|
|
|
|
|
2021-05-29 23:36:13 +02:00
|
|
|
|
;; mu4e is distributed in the hope that it will be useful,
|
2012-04-23 18:07:20 +02:00
|
|
|
|
;; 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
|
2021-05-29 23:36:13 +02:00
|
|
|
|
;; along with mu4e. If not, see <http://www.gnu.org/licenses/>.
|
2012-04-23 18:07:20 +02:00
|
|
|
|
|
|
|
|
|
;;; Commentary:
|
|
|
|
|
|
|
|
|
|
;; In this file are function related to marking messages; they assume we are
|
|
|
|
|
;; currently in the headers buffer.
|
|
|
|
|
|
2019-03-02 10:46:34 +01:00
|
|
|
|
;;; Code:
|
2020-02-11 15:10:35 +01:00
|
|
|
|
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(require 'mu4e-server)
|
2012-09-26 16:28:30 +02:00
|
|
|
|
(require 'mu4e-message)
|
2021-08-29 16:30:10 +02:00
|
|
|
|
(require 'mu4e-folders)
|
2013-12-17 06:59:47 +01:00
|
|
|
|
|
2013-06-29 12:06:51 +02:00
|
|
|
|
;; keep byte-compiler happy
|
|
|
|
|
(declare-function mu4e~headers-mark "mu4e-headers")
|
|
|
|
|
(declare-function mu4e~headers-goto-docid "mu4e-headers")
|
|
|
|
|
(declare-function mu4e-headers-next "mu4e-headers")
|
|
|
|
|
|
2020-02-18 22:39:30 +01:00
|
|
|
|
;;; Variables & constants
|
2020-02-11 15:10:35 +01:00
|
|
|
|
|
2012-04-30 16:36:38 +02:00
|
|
|
|
(defcustom mu4e-headers-leave-behavior 'ask
|
2024-04-09 18:55:03 +02:00
|
|
|
|
"What to do when user leaves the current headers view.
|
|
|
|
|
|
|
|
|
|
\"Leaving\" here means quitting the headers views, refreshing it
|
|
|
|
|
or even quitting mu4e or Emacs.
|
|
|
|
|
|
2012-11-05 17:16:46 +01:00
|
|
|
|
Value is one of the following symbols:
|
|
|
|
|
- `ask' ask user whether to ignore the marks
|
|
|
|
|
- `apply' automatically apply the marks before doing anything else
|
|
|
|
|
- `ignore' automatically ignore the marks without asking"
|
2023-09-28 11:52:39 +02:00
|
|
|
|
:type '(choice (const :tag "ask user whether to ignore marks" ask)
|
|
|
|
|
(const :tag "apply marks without asking" apply)
|
|
|
|
|
(const :tag "ignore marks without asking" ignore))
|
2012-04-30 16:36:38 +02:00
|
|
|
|
:group 'mu4e-headers)
|
|
|
|
|
|
2016-01-30 13:10:23 +01:00
|
|
|
|
(defcustom mu4e-mark-execute-pre-hook nil
|
2019-03-02 10:46:34 +01:00
|
|
|
|
"Hook run just *before* a mark is applied to a message.
|
|
|
|
|
The hook function is called with two arguments, the mark being
|
|
|
|
|
executed and the message itself."
|
|
|
|
|
:type 'hook
|
|
|
|
|
:group 'mu4e-headers)
|
2016-01-30 13:10:23 +01:00
|
|
|
|
|
2012-06-14 10:13:00 +02:00
|
|
|
|
(defvar mu4e-headers-show-target t
|
2022-05-27 20:00:37 +02:00
|
|
|
|
"Whether to show targets (such as \"-> delete\", \"-> /archive\")
|
|
|
|
|
when marking message. Normally, this is useful information for
|
|
|
|
|
the user, however, when you often mark large numbers (thousands)
|
|
|
|
|
of message, showing the target makes this quite a bit
|
|
|
|
|
slower (showing the target uses Emacs overlays, which can be slow
|
|
|
|
|
when overused).")
|
2012-06-14 10:13:00 +02:00
|
|
|
|
|
2020-02-11 15:10:35 +01:00
|
|
|
|
;;; Insert stuff
|
|
|
|
|
|
2022-01-23 09:29:45 +01:00
|
|
|
|
(defvar mu4e--mark-map nil
|
2019-03-02 10:46:34 +01:00
|
|
|
|
"Contains a mapping of docid->markinfo.
|
|
|
|
|
When a message is marked, the information is added here. markinfo
|
2022-05-27 20:00:37 +02:00
|
|
|
|
is a cons cell consisting of the following: (mark . target) where
|
|
|
|
|
MARK is the type of mark (move, trash, delete) TARGET (optional)
|
|
|
|
|
is the target directory (for \"move\")")
|
2012-04-23 18:07:20 +02:00
|
|
|
|
|
|
|
|
|
;; the mark-map is specific for the current header buffer
|
|
|
|
|
;; currently, there can't be more than one, but we never know what will
|
|
|
|
|
;; happen in the future
|
|
|
|
|
|
2012-04-30 16:36:38 +02:00
|
|
|
|
;; the fringe is the space on the left of headers, where we put marks below some
|
|
|
|
|
;; handy definitions; only `mu4e-mark-fringe-len' should be change (if ever),
|
|
|
|
|
;; the others follow from that.
|
2022-01-23 09:29:45 +01:00
|
|
|
|
(defconst mu4e--mark-fringe-len 2
|
2012-04-30 16:36:38 +02:00
|
|
|
|
"Width of the fringe for marks on the left.")
|
2022-01-23 09:29:45 +01:00
|
|
|
|
(defconst mu4e--mark-fringe (make-string mu4e--mark-fringe-len ?\s)
|
2012-04-30 16:36:38 +02:00
|
|
|
|
"The space on the left of message headers to put marks.")
|
2022-01-23 09:29:45 +01:00
|
|
|
|
(defconst mu4e--mark-fringe-format (format "%%-%ds" mu4e--mark-fringe-len)
|
2012-04-30 16:36:38 +02:00
|
|
|
|
"Format string to set a mark and leave remaining space.")
|
|
|
|
|
|
2022-01-23 09:29:45 +01:00
|
|
|
|
(defun mu4e--mark-initialize ()
|
2019-03-02 10:46:34 +01:00
|
|
|
|
"Initialize the marks-subsystem."
|
2024-04-09 18:55:03 +02:00
|
|
|
|
(set (make-local-variable 'mu4e--mark-map) (make-hash-table))
|
|
|
|
|
;; ask user when kill buffer / emacs with live marks.
|
|
|
|
|
;; (subject to mu4e-headers-leave-behavior)
|
|
|
|
|
(add-hook 'kill-buffer-query-functions
|
|
|
|
|
#'mu4e-mark-handle-when-leaving nil t)
|
|
|
|
|
(add-hook 'kill-emacs-query-functions
|
|
|
|
|
#'mu4e-mark-handle-when-leaving nil t))
|
2012-04-23 18:07:20 +02:00
|
|
|
|
|
2022-01-23 09:29:45 +01:00
|
|
|
|
(defun mu4e--mark-clear ()
|
2019-03-02 10:46:34 +01:00
|
|
|
|
"Clear the marks-subsystem."
|
2022-01-23 09:29:45 +01:00
|
|
|
|
(clrhash mu4e--mark-map))
|
2012-04-28 12:46:38 +02:00
|
|
|
|
|
2022-01-23 09:29:45 +01:00
|
|
|
|
(defun mu4e--mark-find-headers-buffer ()
|
2013-06-29 12:06:51 +02:00
|
|
|
|
"Find the headers buffer, if any."
|
2022-12-06 10:46:39 +01:00
|
|
|
|
(seq-find (lambda (_)
|
2023-01-18 23:36:53 +01:00
|
|
|
|
(mu4e-current-buffer-type-p 'headers))
|
|
|
|
|
(buffer-list)))
|
2015-04-26 16:09:06 +02:00
|
|
|
|
|
2022-01-23 09:29:45 +01:00
|
|
|
|
(defmacro mu4e--mark-in-context (&rest body)
|
2019-03-02 10:46:34 +01:00
|
|
|
|
"Evaluate BODY in the context of the headers buffer.
|
|
|
|
|
The current buffer must be either a headers or view buffer."
|
2017-03-18 09:09:10 +01:00
|
|
|
|
`(cond
|
2022-11-18 13:54:27 +01:00
|
|
|
|
((mu4e-current-buffer-type-p 'headers) ,@body)
|
|
|
|
|
((mu4e-current-buffer-type-p 'view)
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(when (buffer-live-p (mu4e-get-headers-buffer))
|
|
|
|
|
(let* ((msg (mu4e-message-at-point))
|
|
|
|
|
(docid (mu4e-message-field msg :docid)))
|
|
|
|
|
(with-current-buffer (mu4e-get-headers-buffer)
|
2022-11-30 16:32:45 +01:00
|
|
|
|
(when (mu4e~headers-goto-docid docid)
|
|
|
|
|
,@body
|
|
|
|
|
)))))))
|
2013-06-29 12:06:51 +02:00
|
|
|
|
|
2019-03-02 10:46:34 +01:00
|
|
|
|
(defconst mu4e-marks
|
2020-02-11 12:00:46 +01:00
|
|
|
|
'((refile
|
|
|
|
|
:char ("r" . "▶")
|
|
|
|
|
:prompt "refile"
|
|
|
|
|
:dyn-target (lambda (target msg) (mu4e-get-refile-folder msg))
|
|
|
|
|
:action (lambda (docid msg target)
|
2022-01-23 09:29:45 +01:00
|
|
|
|
(mu4e--server-move docid (mu4e--mark-check-target target) "-N")))
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(delete
|
|
|
|
|
:char ("D" . "x")
|
|
|
|
|
:prompt "Delete"
|
|
|
|
|
:show-target (lambda (target) "delete")
|
2021-08-29 18:53:53 +02:00
|
|
|
|
:action (lambda (docid msg target) (mu4e--server-remove docid)))
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(flag
|
|
|
|
|
:char ("+" . "✚")
|
|
|
|
|
:prompt "+flag"
|
|
|
|
|
:show-target (lambda (target) "flag")
|
|
|
|
|
:action (lambda (docid msg target)
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(mu4e--server-move docid nil "+F-u-N")))
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(move
|
|
|
|
|
:char ("m" . "▷")
|
|
|
|
|
:prompt "move"
|
2022-01-23 09:29:45 +01:00
|
|
|
|
:ask-target mu4e--mark-get-move-target
|
2020-02-11 12:00:46 +01:00
|
|
|
|
:action (lambda (docid msg target)
|
2022-01-23 09:29:45 +01:00
|
|
|
|
(mu4e--server-move docid (mu4e--mark-check-target target) "-N")))
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(read
|
|
|
|
|
:char ("!" . "◼")
|
|
|
|
|
:prompt "!read"
|
|
|
|
|
:show-target (lambda (target) "read")
|
2021-08-29 18:53:53 +02:00
|
|
|
|
:action (lambda (docid msg target) (mu4e--server-move docid nil "+S-u-N")))
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(trash
|
|
|
|
|
:char ("d" . "▼")
|
|
|
|
|
:prompt "dtrash"
|
|
|
|
|
:dyn-target (lambda (target msg) (mu4e-get-trash-folder msg))
|
2022-01-23 09:29:45 +01:00
|
|
|
|
:action (lambda (docid msg target)
|
2023-01-18 23:36:53 +01:00
|
|
|
|
(mu4e--server-move docid
|
2022-01-23 09:29:45 +01:00
|
|
|
|
(mu4e--mark-check-target target) "+T-N")))
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(unflag
|
|
|
|
|
:char ("-" . "➖")
|
|
|
|
|
:prompt "-unflag"
|
|
|
|
|
:show-target (lambda (target) "unflag")
|
2021-08-29 18:53:53 +02:00
|
|
|
|
:action (lambda (docid msg target) (mu4e--server-move docid nil "-F-N")))
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(untrash
|
|
|
|
|
:char ("=" . "▲")
|
|
|
|
|
:prompt "=untrash"
|
|
|
|
|
:show-target (lambda (target) "untrash")
|
2021-08-29 18:53:53 +02:00
|
|
|
|
:action (lambda (docid msg target) (mu4e--server-move docid nil "-T")))
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(unread
|
|
|
|
|
:char ("?" . "◻")
|
|
|
|
|
:prompt "?unread"
|
|
|
|
|
:show-target (lambda (target) "unread")
|
2021-08-29 18:53:53 +02:00
|
|
|
|
:action (lambda (docid msg target) (mu4e--server-move docid nil "-S+u-N")))
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(unmark
|
|
|
|
|
:char " "
|
|
|
|
|
:prompt "unmark"
|
|
|
|
|
:action (mu4e-error "No action for unmarking"))
|
|
|
|
|
(action
|
|
|
|
|
:char ( "a" . "◯")
|
|
|
|
|
:prompt "action"
|
|
|
|
|
:ask-target (lambda () (mu4e-read-option "Action: " mu4e-headers-actions))
|
|
|
|
|
:action (lambda (docid msg actionfunc)
|
|
|
|
|
(save-excursion
|
|
|
|
|
(when (mu4e~headers-goto-docid docid)
|
|
|
|
|
(mu4e-headers-action actionfunc)))))
|
|
|
|
|
(something
|
|
|
|
|
:char ("*" . "✱")
|
|
|
|
|
:prompt "*something"
|
|
|
|
|
:action (mu4e-error "No action for deferred mark")))
|
2017-03-18 09:09:10 +01:00
|
|
|
|
|
2015-12-15 08:07:17 +01:00
|
|
|
|
"The list of all the possible marks.
|
|
|
|
|
This is an alist mapping mark symbols to their properties. The
|
|
|
|
|
properties are:
|
|
|
|
|
:char (string) or (basic . fancy) The character to display in
|
|
|
|
|
the headers view. Either a single-character string, or a
|
|
|
|
|
dotted-pair cons cell where the second item will be used if
|
2019-03-02 10:46:34 +01:00
|
|
|
|
`mu4e-use-fancy-chars' is t, otherwise we'll use
|
2015-12-15 08:07:17 +01:00
|
|
|
|
the first one. It can also be a plain string for backwards
|
|
|
|
|
compatibility since we didn't always support
|
|
|
|
|
`mu4e-use-fancy-chars' here.
|
|
|
|
|
:prompt (string) The prompt to use when asking for marks (used for
|
|
|
|
|
example when marking a whole thread)
|
|
|
|
|
:ask-target (function returning a string) Get the target. This
|
|
|
|
|
function run once per bulk-operation, and thus is suitable
|
|
|
|
|
for user-interaction. If nil, the target is nil.
|
|
|
|
|
:dyn-target (function from (TARGET MSG) to string). Compute
|
|
|
|
|
the dynamic target. This is run once per message, which is
|
|
|
|
|
passed as MSG. The default is to just return the target.
|
|
|
|
|
:show-target (function from TARGET to string) How to display
|
|
|
|
|
the target.
|
|
|
|
|
:action (function taking (DOCID MSG TARGET)). The action to
|
|
|
|
|
apply on the message.")
|
2014-11-26 21:27:56 +01:00
|
|
|
|
|
2014-11-27 11:10:51 +01:00
|
|
|
|
(defun mu4e-mark-at-point (mark target)
|
2012-11-10 14:01:17 +01:00
|
|
|
|
"Mark (or unmark) message at point.
|
|
|
|
|
MARK specifies the mark-type. For `move'-marks and `trash'-marks
|
2022-05-27 20:00:37 +02:00
|
|
|
|
the TARGET argument is non-nil and specifies to which maildir the
|
|
|
|
|
message is to be moved/trashed. The function works in both
|
|
|
|
|
headers buffers and message buffers.
|
2012-04-23 18:07:20 +02:00
|
|
|
|
|
|
|
|
|
The following marks are available, and the corresponding props:
|
|
|
|
|
|
|
|
|
|
MARK TARGET description
|
|
|
|
|
----------------------------------------------------------
|
2020-02-11 12:00:46 +01:00
|
|
|
|
`refile' y mark this message for archiving
|
|
|
|
|
`something' n mark this message for *something* (decided later)
|
|
|
|
|
`delete' n remove the message
|
|
|
|
|
`flag' n mark this message for flagging
|
|
|
|
|
`move' y move the message to some folder
|
|
|
|
|
`read' n mark the message as read
|
|
|
|
|
`trash' y trash the message to some folder
|
|
|
|
|
`unflag' n mark this message for unflagging
|
2022-05-27 20:00:37 +02:00
|
|
|
|
`untrash' n remove the `trashed' flag from a message
|
2020-02-11 12:00:46 +01:00
|
|
|
|
`unmark' n unmark this message
|
|
|
|
|
`unread' n mark the message as unread
|
2015-04-26 16:09:06 +02:00
|
|
|
|
`action' y mark the message for some action."
|
2012-04-23 18:07:20 +02:00
|
|
|
|
(interactive)
|
2012-09-26 16:28:30 +02:00
|
|
|
|
(let* ((msg (mu4e-message-at-point))
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(docid (mu4e-message-field msg :docid))
|
2022-05-27 20:00:37 +02:00
|
|
|
|
;; get a cell with the mark char and the "move" already has a target
|
|
|
|
|
;; (the target folder) the other ones get a pseudo "target", as info
|
|
|
|
|
;; for the user.
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(markdesc (cdr (or (assq mark mu4e-marks)
|
|
|
|
|
(mu4e-error "Invalid mark %S" mark))))
|
|
|
|
|
(get-markkar
|
|
|
|
|
(lambda (char)
|
|
|
|
|
(if (listp char)
|
|
|
|
|
(if mu4e-use-fancy-chars (cdr char) (car char))
|
|
|
|
|
char)))
|
|
|
|
|
(markkar (funcall get-markkar (plist-get markdesc :char)))
|
2022-01-23 09:29:45 +01:00
|
|
|
|
(target (mu4e--mark-get-dyn-target mark target))
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(show-fct (plist-get markdesc :show-target))
|
|
|
|
|
(shown-target (if show-fct
|
|
|
|
|
(funcall show-fct target)
|
|
|
|
|
(if target (format "%S" target)))))
|
2012-09-06 09:33:53 +02:00
|
|
|
|
(unless docid (mu4e-warn "No message on this line"))
|
2017-09-17 12:18:06 +02:00
|
|
|
|
(unless (eq major-mode 'mu4e-headers-mode)
|
|
|
|
|
(mu4e-error "Not in headers-mode"))
|
2012-04-23 18:07:20 +02:00
|
|
|
|
(save-excursion
|
2012-05-01 21:45:54 +02:00
|
|
|
|
(when (mu4e~headers-mark docid markkar)
|
2020-02-11 12:00:46 +01:00
|
|
|
|
;; update the hash -- remove everything current, and if add the new
|
|
|
|
|
;; stuff, unless we're unmarking
|
2022-01-23 09:29:45 +01:00
|
|
|
|
(remhash docid mu4e--mark-map)
|
2021-06-10 14:19:22 +02:00
|
|
|
|
;; remove possible mark overlays
|
2022-01-23 09:29:45 +01:00
|
|
|
|
(remove-overlays (line-beginning-position) (line-end-position)
|
2023-01-18 23:36:53 +01:00
|
|
|
|
'mu4e-mark t)
|
2020-02-11 12:00:46 +01:00
|
|
|
|
;; now, let's set a mark (unless we were unmarking)
|
|
|
|
|
(unless (eql mark 'unmark)
|
2022-01-23 09:29:45 +01:00
|
|
|
|
(puthash docid (cons mark target) mu4e--mark-map)
|
2020-02-11 12:00:46 +01:00
|
|
|
|
;; when we have a target (ie., when moving), show the target folder in
|
|
|
|
|
;; an overlay
|
|
|
|
|
(when (and shown-target mu4e-headers-show-target)
|
|
|
|
|
(let* ((targetstr (propertize (concat "-> " shown-target " ")
|
|
|
|
|
'face 'mu4e-system-face))
|
|
|
|
|
;; mu4e~headers-goto-docid docid t \will take us just after
|
2022-01-23 09:29:45 +01:00
|
|
|
|
;; the docid cookie and then we skip the mu4e--mark-fringe
|
|
|
|
|
(start (+ (length mu4e--mark-fringe)
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(mu4e~headers-goto-docid docid t)))
|
|
|
|
|
(overlay (make-overlay start (+ start (length targetstr)))))
|
|
|
|
|
(overlay-put overlay 'display targetstr)
|
2021-06-17 21:11:38 +02:00
|
|
|
|
(overlay-put overlay 'mu4e-mark t)
|
2021-06-10 14:19:22 +02:00
|
|
|
|
(overlay-put overlay 'evaporate t)
|
2020-02-11 12:00:46 +01:00
|
|
|
|
docid)))))))
|
2012-04-23 18:07:20 +02:00
|
|
|
|
|
2022-01-23 09:29:45 +01:00
|
|
|
|
(defun mu4e--mark-get-move-target ()
|
2014-11-27 11:10:51 +01:00
|
|
|
|
"Ask for a move target, and propose to create it if it does not exist."
|
|
|
|
|
(let* ((target (mu4e-ask-maildir "Move message to: "))
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(target (if (string= (substring target 0 1) "/")
|
|
|
|
|
target
|
|
|
|
|
(concat "/" target)))
|
2023-01-27 06:22:20 +01:00
|
|
|
|
(fulltarget (mu4e-join-paths (mu4e-root-maildir) target)))
|
2023-02-12 10:17:59 +01:00
|
|
|
|
(when (mu4e-create-maildir-maybe fulltarget)
|
2012-09-27 11:01:26 +02:00
|
|
|
|
target)))
|
|
|
|
|
|
2022-01-23 09:29:45 +01:00
|
|
|
|
(defun mu4e--mark-ask-target (mark)
|
2014-11-27 11:10:51 +01:00
|
|
|
|
"Ask the target for MARK, if the user should be asked the target."
|
|
|
|
|
(let ((getter (plist-get (cdr (assq mark mu4e-marks)) :ask-target)))
|
|
|
|
|
(and getter (funcall getter))))
|
|
|
|
|
|
2022-01-23 09:29:45 +01:00
|
|
|
|
(defun mu4e--mark-get-dyn-target (mark target)
|
2019-03-02 10:46:34 +01:00
|
|
|
|
"Get the dynamic TARGET for MARK.
|
|
|
|
|
The result may depend on the message at point."
|
2014-11-27 11:10:51 +01:00
|
|
|
|
(let ((getter (plist-get (cdr (assq mark mu4e-marks)) :dyn-target)))
|
|
|
|
|
(if getter
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(funcall getter target (mu4e-message-at-point))
|
2014-11-27 11:10:51 +01:00
|
|
|
|
target)))
|
2012-10-13 21:13:17 +02:00
|
|
|
|
|
2012-09-27 11:01:26 +02:00
|
|
|
|
(defun mu4e-mark-set (mark &optional target)
|
2019-03-02 10:46:34 +01:00
|
|
|
|
"Mark the header at point with MARK or all in the region.
|
|
|
|
|
Optionally, provide TARGET (for moves)."
|
2012-10-29 11:25:07 +01:00
|
|
|
|
(unless target
|
2022-01-23 09:29:45 +01:00
|
|
|
|
(setq target (mu4e--mark-ask-target mark)))
|
2012-10-13 21:13:17 +02:00
|
|
|
|
(if (not (use-region-p))
|
2020-02-11 12:00:46 +01:00
|
|
|
|
;; single message
|
|
|
|
|
(mu4e-mark-at-point mark target)
|
2012-10-13 21:13:17 +02:00
|
|
|
|
;; mark all messages in the region.
|
|
|
|
|
(save-excursion
|
|
|
|
|
(let ((cant-go-further) (eor (region-end)))
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(goto-char (region-beginning))
|
|
|
|
|
(while (and (< (point) eor) (not cant-go-further))
|
|
|
|
|
(mu4e-mark-at-point mark target)
|
|
|
|
|
(setq cant-go-further (not (mu4e-headers-next))))))))
|
2012-10-13 21:13:17 +02:00
|
|
|
|
|
2012-09-27 11:01:26 +02:00
|
|
|
|
(defun mu4e-mark-restore (docid)
|
|
|
|
|
"Restore the visual mark for the message with DOCID."
|
2022-01-23 09:29:45 +01:00
|
|
|
|
(let ((markcell (gethash docid mu4e--mark-map)))
|
2012-09-27 11:01:26 +02:00
|
|
|
|
(when markcell
|
|
|
|
|
(save-excursion
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(when (mu4e~headers-goto-docid docid)
|
|
|
|
|
(mu4e-mark-at-point (car markcell) (cdr markcell)))))))
|
2012-04-23 18:07:20 +02:00
|
|
|
|
|
2022-01-23 09:29:45 +01:00
|
|
|
|
(defun mu4e--mark-get-markpair (prompt &optional allow-something)
|
2019-03-02 10:46:34 +01:00
|
|
|
|
"Ask user with PROMPT for a mark and return (MARK . TARGET).
|
2022-05-27 20:00:37 +02:00
|
|
|
|
If ALLOW-SOMETHING is non-nil, allow the `something' pseudo mark
|
2012-11-10 14:01:17 +01:00
|
|
|
|
as well."
|
2015-04-26 16:09:06 +02:00
|
|
|
|
(let* ((marks (mapcar (lambda (markdescr)
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(cons (plist-get (cdr markdescr) :prompt)
|
|
|
|
|
(car markdescr)))
|
|
|
|
|
mu4e-marks))
|
|
|
|
|
(marks
|
|
|
|
|
(if allow-something
|
2022-01-23 09:29:45 +01:00
|
|
|
|
marks (seq-remove (lambda (m) (eq 'something (cdr m))) marks)))
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(mark (mu4e-read-option prompt marks))
|
2022-01-23 09:29:45 +01:00
|
|
|
|
(target (mu4e--mark-ask-target mark)))
|
2012-06-14 18:10:02 +02:00
|
|
|
|
(cons mark target)))
|
|
|
|
|
|
|
|
|
|
(defun mu4e-mark-resolve-deferred-marks ()
|
2019-03-02 10:46:34 +01:00
|
|
|
|
"Check if there are any deferred ('something') mark-instances.
|
2012-11-10 14:01:17 +01:00
|
|
|
|
If there are such marks, replace them with a _real_ mark (ask the
|
|
|
|
|
user which one)."
|
2012-06-14 18:10:02 +02:00
|
|
|
|
(interactive)
|
2022-01-23 09:29:45 +01:00
|
|
|
|
(mu4e--mark-in-context
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(let ((markpair))
|
|
|
|
|
(maphash
|
|
|
|
|
(lambda (docid val)
|
|
|
|
|
(let ((mark (car val)))
|
|
|
|
|
(when (eql mark 'something)
|
|
|
|
|
(unless markpair
|
|
|
|
|
(setq markpair
|
2022-01-23 09:29:45 +01:00
|
|
|
|
(mu4e--mark-get-markpair "Set deferred mark(s) to: " nil)))
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(save-excursion
|
|
|
|
|
(when (mu4e~headers-goto-docid docid)
|
|
|
|
|
(mu4e-mark-set (car markpair) (cdr markpair)))))))
|
2022-01-23 09:29:45 +01:00
|
|
|
|
mu4e--mark-map))))
|
2012-10-08 23:15:02 +02:00
|
|
|
|
|
2022-01-23 09:29:45 +01:00
|
|
|
|
(defun mu4e--mark-check-target (target)
|
2019-03-02 10:46:34 +01:00
|
|
|
|
"Check if TARGET exists; if not, offer to create it."
|
2023-01-27 06:22:20 +01:00
|
|
|
|
(let ((fulltarget (mu4e-join-paths (mu4e-root-maildir) target)))
|
2012-10-08 23:15:02 +02:00
|
|
|
|
(if (not (mu4e-create-maildir-maybe fulltarget))
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(mu4e-error "Target dir %s does not exist " fulltarget)
|
2012-10-08 23:15:02 +02:00
|
|
|
|
target)))
|
|
|
|
|
|
2021-04-12 21:59:52 +02:00
|
|
|
|
(defun mu4e-mark-execute-all (&optional no-confirmation)
|
2012-11-10 14:01:17 +01:00
|
|
|
|
"Execute the actions for all marked messages in this buffer.
|
2019-11-06 16:13:39 +01:00
|
|
|
|
After the actions have been executed successfully, the affected
|
2012-11-10 14:01:17 +01:00
|
|
|
|
messages are *hidden* from the current header list. Since the
|
|
|
|
|
headers are the result of a search, we cannot be certain that the
|
2014-08-08 13:18:29 +02:00
|
|
|
|
messages no longer match the current one - to get that
|
2012-04-23 18:07:20 +02:00
|
|
|
|
certainty, we need to rerun the search, but we don't want to do
|
2014-08-08 13:18:29 +02:00
|
|
|
|
that automatically, as it may be too slow and/or break the user's
|
2012-04-23 18:07:20 +02:00
|
|
|
|
flow. Therefore, we hide the message, which in practice seems to
|
2012-04-28 12:46:38 +02:00
|
|
|
|
work well.
|
|
|
|
|
|
|
|
|
|
If NO-CONFIRMATION is non-nil, don't ask user for confirmation."
|
2021-04-12 17:01:38 +02:00
|
|
|
|
(interactive "P")
|
2022-01-23 09:29:45 +01:00
|
|
|
|
(mu4e--mark-in-context
|
2023-01-18 23:36:53 +01:00
|
|
|
|
(let* ((marknum (mu4e-mark-marks-num))
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(prompt (format "Are you sure you want to execute %d mark%s?"
|
|
|
|
|
marknum (if (> marknum 1) "s" ""))))
|
2016-01-30 13:04:45 +01:00
|
|
|
|
(if (zerop marknum)
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(mu4e-warn "Nothing is marked")
|
2016-01-30 13:04:45 +01:00
|
|
|
|
(mu4e-mark-resolve-deferred-marks)
|
2019-03-02 10:46:34 +01:00
|
|
|
|
(when (or no-confirmation (y-or-n-p prompt))
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(maphash
|
|
|
|
|
(lambda (docid val)
|
|
|
|
|
(let* ((mark (car val)) (target (cdr val))
|
|
|
|
|
(markdescr (assq mark mu4e-marks))
|
|
|
|
|
(msg (save-excursion
|
|
|
|
|
(mu4e~headers-goto-docid docid)
|
|
|
|
|
(mu4e-message-at-point))))
|
|
|
|
|
;; note: whenever you do something with the message,
|
|
|
|
|
;; it looses its N (new) flag
|
|
|
|
|
(if markdescr
|
|
|
|
|
(progn
|
|
|
|
|
(run-hook-with-args
|
|
|
|
|
'mu4e-mark-execute-pre-hook mark msg)
|
|
|
|
|
(funcall (plist-get (cdr markdescr) :action)
|
|
|
|
|
docid msg target))
|
|
|
|
|
(mu4e-error "Unrecognized mark %S" mark))))
|
2022-01-23 09:29:45 +01:00
|
|
|
|
mu4e--mark-map))
|
2023-01-18 23:36:53 +01:00
|
|
|
|
(mu4e-mark-unmark-all 'no-confirm)
|
2016-01-30 13:04:45 +01:00
|
|
|
|
(message nil)))))
|
2012-04-28 12:46:38 +02:00
|
|
|
|
|
2023-01-18 23:36:53 +01:00
|
|
|
|
(defun mu4e-mark-unmark-all (&optional no-confirmation)
|
2012-04-23 18:07:20 +02:00
|
|
|
|
"Unmark all marked messages."
|
|
|
|
|
(interactive)
|
2022-01-23 09:29:45 +01:00
|
|
|
|
(mu4e--mark-in-context
|
2023-01-18 23:36:53 +01:00
|
|
|
|
(when (zerop (mu4e-mark-marks-num))
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(mu4e-warn "Nothing is marked"))
|
2023-01-18 23:36:53 +01:00
|
|
|
|
(let* ((marknum (hash-table-count mu4e--mark-map))
|
|
|
|
|
(prompt (format "Are you sure you want to unmark %d message%s?"
|
|
|
|
|
marknum (if (> marknum 1) "s" ""))))
|
|
|
|
|
(when (or no-confirmation (y-or-n-p prompt))
|
|
|
|
|
(maphash
|
|
|
|
|
(lambda (docid _val)
|
|
|
|
|
(save-excursion
|
|
|
|
|
(when (mu4e~headers-goto-docid docid)
|
|
|
|
|
(mu4e-mark-set 'unmark))))
|
|
|
|
|
mu4e--mark-map)
|
|
|
|
|
;; in any case, clear the marks map
|
|
|
|
|
(mu4e--mark-clear)))))
|
2012-10-13 21:13:17 +02:00
|
|
|
|
|
2012-04-23 18:07:20 +02:00
|
|
|
|
(defun mu4e-mark-docid-marked-p (docid)
|
2019-03-02 10:46:34 +01:00
|
|
|
|
"Is the given DOCID marked?"
|
2022-01-23 09:29:45 +01:00
|
|
|
|
(when (gethash docid mu4e--mark-map) t))
|
2020-02-11 15:10:35 +01:00
|
|
|
|
|
2012-10-24 22:49:26 +02:00
|
|
|
|
(defun mu4e-mark-marks-num ()
|
2019-03-02 10:46:34 +01:00
|
|
|
|
"Return the number of mark-instances in the current buffer."
|
2022-01-23 09:29:45 +01:00
|
|
|
|
(mu4e--mark-in-context
|
|
|
|
|
(if mu4e--mark-map (hash-table-count mu4e--mark-map) 0)))
|
2012-04-23 18:07:20 +02:00
|
|
|
|
|
|
|
|
|
(defun mu4e-mark-handle-when-leaving ()
|
2019-03-02 10:46:34 +01:00
|
|
|
|
"Handle any mark-instances in the current buffer when leaving.
|
2022-05-27 20:00:37 +02:00
|
|
|
|
This is done according to the value of
|
|
|
|
|
`mu4e-headers-leave-behavior'. This function is to be called
|
|
|
|
|
before any further action (like searching, quitting the buffer)
|
|
|
|
|
is taken; returning t means \"take the following action\", return
|
|
|
|
|
nil means \"don't do anything\"."
|
2022-01-23 09:29:45 +01:00
|
|
|
|
(mu4e--mark-in-context
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(let ((marknum (mu4e-mark-marks-num))
|
|
|
|
|
(what mu4e-headers-leave-behavior))
|
|
|
|
|
(unless (zerop marknum) ;; nothing to do?
|
|
|
|
|
(when (eq what 'ask)
|
|
|
|
|
(setq what (mu4e-read-option
|
|
|
|
|
(format "There are %d existing mark(s); should we: "
|
|
|
|
|
marknum)
|
|
|
|
|
'( ("apply marks" . apply)
|
|
|
|
|
("ignore marks?" . ignore)))))
|
|
|
|
|
;; we determined what to do... now do it
|
|
|
|
|
(when (eq what 'apply)
|
2024-04-09 18:55:03 +02:00
|
|
|
|
(mu4e-mark-execute-all t)))))
|
|
|
|
|
t) ;; return t for compat with `kill-buffer-query-functions
|
2015-04-26 16:09:06 +02:00
|
|
|
|
|
2020-02-11 13:26:45 +01:00
|
|
|
|
;;; _
|
2012-04-23 18:07:20 +02:00
|
|
|
|
(provide 'mu4e-mark)
|
2019-03-02 10:46:34 +01:00
|
|
|
|
;;; mu4e-mark.el ends here
|