* implement / document changing sort order and threading

- update the protocol a bit (mu4e-proc, mu-cmd-server)
  - provide the user-interface (mu4e-headers.el)
  - document it (mu4e.texi, mu-server.1)
  - some cosmetics (the other changes)
This commit is contained in:
djcb
2012-06-10 11:19:51 +03:00
parent f37de2174c
commit 20d858e464
8 changed files with 233 additions and 77 deletions

View File

@ -83,6 +83,18 @@ are of the form:
* NAME is the name of the action (e.g. \"Count lines\") * NAME is the name of the action (e.g. \"Count lines\")
* SHORTCUT is a one-character shortcut to call this action * SHORTCUT is a one-character shortcut to call this action
* FUNC is a function which receives a message plist as an argument.") * FUNC is a function which receives a message plist as an argument.")
(defvar mu4e-headers-sortfield 'date
"Field to sort the headers by. Field must be a symbol, one of:
date, subject, size, prio, from, to.")
(defvar mu4e-headers-sort-revert t
"Whether to revert the sort-order, i.e. Z->A instead of A-Z. When
sorting by date, it's useful to go from biggest to smallest, so
newest messages come first.")
(defvar mu4e-headers-show-threads t
"Whether to show threads in the headers list.")
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -100,6 +112,20 @@ are of the form:
(defvar mu4e~headers-view-win nil (defvar mu4e~headers-view-win nil
"The view window connected to this headers view.") "The view window connected to this headers view.")
(defvar mu4e~headers-sortfield-choices
'( ("date" nil date)
("from" nil from)
("prio" nil prio)
("size" ?z size)
("subject" nil subject)
("to" nil to))
"List of cells describing the various sort-options (in the format
needed for `mu4e-read-option'.")
(defvar mu4e~headers-full-search nil
"Whether the last search was a 'full search'.")
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun mu4e~headers-clear () (defun mu4e~headers-clear ()
"Clear the header buffer and related data structures." "Clear the header buffer and related data structures."
@ -222,7 +248,7 @@ if provided, or at the end of the buffer otherwise."
(:subject (:subject
(concat ;; prefix subject with a thread indicator (concat ;; prefix subject with a thread indicator
(mu4e~headers-thread-prefix (plist-get msg :thread)) (mu4e~headers-thread-prefix (plist-get msg :thread))
;; "["(plist-get (plist-get msg :thread) :path) "] " ;; "["(plist-get (plist-get msg :thread) :path) "] "
val)) val))
((:maildir :path) val) ((:maildir :path) val)
((:to :from :cc :bcc) (mu4e~headers-contact-str val)) ((:to :from :cc :bcc) (mu4e~headers-contact-str val))
@ -231,10 +257,14 @@ if provided, or at the end of the buffer otherwise."
(:from-or-to (:from-or-to
(let* ((from-lst (plist-get msg :from)) (let* ((from-lst (plist-get msg :from))
(from (and from-lst (cdar from-lst)))) (from (and from-lst (cdar from-lst))))
(if (and from (string-match mu4e-user-mail-address-regexp from)) (if (and from
(concat (or (cdr-safe mu4e-headers-from-or-to-prefix)) (string-match mu4e-user-mail-address-regexp
from))
(concat (or (cdr-safe
mu4e-headers-from-or-to-prefix))
(mu4e~headers-contact-str (plist-get msg :to))) (mu4e~headers-contact-str (plist-get msg :to)))
(concat (or (car-safe mu4e-headers-from-or-to-prefix)) (concat (or (car-safe
mu4e-headers-from-or-to-prefix))
(mu4e~headers-contact-str from-lst))))) (mu4e~headers-contact-str from-lst)))))
(:date (format-time-string mu4e-headers-date-format val)) (:date (format-time-string mu4e-headers-date-format val))
(:flags (mu4e-flags-to-string val)) (:flags (mu4e-flags-to-string val))
@ -312,6 +342,9 @@ after the end of the search results."
(define-key map "b" 'mu4e-headers-search-bookmark) (define-key map "b" 'mu4e-headers-search-bookmark)
(define-key map "B" 'mu4e-headers-search-bookmark-edit) (define-key map "B" 'mu4e-headers-search-bookmark-edit)
(define-key map "O" 'mu4e-headers-change-sorting)
(define-key map "P" 'mu4e-headers-toggle-threading)
(define-key map "q" 'mu4e~headers-kill-buffer-and-window) (define-key map "q" 'mu4e~headers-kill-buffer-and-window)
(define-key map "z" 'mu4e~headers-kill-buffer-and-window) (define-key map "z" 'mu4e~headers-kill-buffer-and-window)
@ -391,8 +424,10 @@ after the end of the search results."
(define-key menumap [unmark-all] '("Unmark all" . mu4e-mark-unmark-all)) (define-key menumap [unmark-all] '("Unmark all" . mu4e-mark-unmark-all))
(define-key menumap [unmark] '("Unmark" . mu4e~headers-mark-unmark)) (define-key menumap [unmark] '("Unmark" . mu4e~headers-mark-unmark))
(define-key menumap [mark-pattern] '("Mark pattern" . mu4e-headers-mark-pattern)) (define-key menumap [mark-pattern] '("Mark pattern" .
(define-key menumap [mark-as-read] '("Mark as read" . mu4e~headers-mark-read)) mu4e-headers-mark-pattern))
(define-key menumap [mark-as-read] '("Mark as read" .
mu4e~headers-mark-read))
(define-key menumap [mark-as-unread] (define-key menumap [mark-as-unread]
'("Mark as unread" . mu4e~headers-mark-unread)) '("Mark as unread" . mu4e~headers-mark-unread))
@ -410,10 +445,14 @@ after the end of the search results."
(define-key menumap [sepa2] '("--")) (define-key menumap [sepa2] '("--"))
(define-key menumap [query-next] '("Next query" . mu4e-headers-query-next)) (define-key menumap [query-next] '("Next query" . mu4e-headers-query-next))
(define-key menumap [query-prev] '("Previous query" . mu4e-headers-query-prev)) (define-key menumap [query-prev] '("Previous query" .
(define-key menumap [narrow-search] '("Narrow search" . mu4e-headers-search-narrow)) mu4e-headers-query-prev))
(define-key menumap [bookmark] '("Search bookmark" . mu4e-headers-search-bookmark)) (define-key menumap [narrow-search] '("Narrow search" .
(define-key menumap [jump] '("Jump to maildir" . mu4e~headers-jump-to-maildir)) mu4e-headers-search-narrow))
(define-key menumap [bookmark] '("Search bookmark" .
mu4e-headers-search-bookmark))
(define-key menumap [jump] '("Jump to maildir" .
mu4e~headers-jump-to-maildir))
(define-key menumap [refresh] '("Refresh" . mu4e-headers-rerun-search)) (define-key menumap [refresh] '("Refresh" . mu4e-headers-rerun-search))
(define-key menumap [search] '("Search" . mu4e-headers-search)) (define-key menumap [search] '("Search" . mu4e-headers-search))
@ -598,10 +637,27 @@ non-nill, don't raise an error when the docid is not found."
(unless ignore-missing (unless ignore-missing
(error "Cannot find message with docid %S" docid))))) (error "Cannot find message with docid %S" docid)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun mu4e~headers-update-global-mode-string ()
"Determine the mode string for the headers buffers (based on the
last query, sorting settings."
(let* ((cell (find-if
(lambda (cell)
(eq (nth 2 cell) mu4e-headers-sortfield))
mu4e~headers-sortfield-choices))
(optchar (or (nth 1 cell) (substring (nth 0 cell) 0 1))))
(setq global-mode-string
(concat
(propertize expr 'face 'mu4e-title-face)
"("
optchar
(if mu4e-headers-sort-revert "d" "a")
(when mu4e-headers-show-threads "T")
(when mu4e~headers-full-search "F")
")"))))
(defun mu4e~headers-search-execute (expr search-all ignore-history) (defun mu4e~headers-search-execute (expr full-search ignore-history)
"Search in the mu database for EXPR, and switch to the output "Search in the mu database for EXPR, and switch to the output
buffer for the results. If SEARCH-ALL is non-nil return all buffer for the results. If FULL-SEARCH is non-nil return all
results, otherwise, limit number of results to results, otherwise, limit number of results to
`mu4e-search-results-limit'. If IGNORE-HISTORY is true, do *not* `mu4e-search-results-limit'. If IGNORE-HISTORY is true, do *not*
update the query history stack." update the query history stack."
@ -613,17 +669,21 @@ update the query history stack."
(mu4e-headers-mode) (mu4e-headers-mode)
(unless ignore-history (unless ignore-history
;; save the old present query to the history list ;; save the old present query to the history list
(when mu4e~headers-query-present (when mu4e~headers-last-query
(mu4e~headers-push-query mu4e~headers-query-present 'past))) (mu4e~headers-push-query mu4e~headers-last-query 'past)))
(setq (setq
global-mode-string (propertize expr 'face 'mu4e-title-face)
mu4e~headers-buffer buf mu4e~headers-buffer buf
mode-name "mu4e-headers" mode-name "mu4e-headers"
mu4e~headers-query-present expr)) mu4e~headers-last-query expr
mu4e~headers-full-search full-search)
(mu4e~headers-update-global-mode-string))
(switch-to-buffer buf) (switch-to-buffer buf)
(mu4e~proc-find (mu4e~proc-find
(replace-regexp-in-string "\"" "\\\\\"" expr) ;; escape "\" (replace-regexp-in-string "\"" "\\\\\"" expr) ;; escape "\"
(unless search-all mu4e-search-results-limit)) mu4e-headers-show-threads
mu4e-headers-sortfield
mu4e-headers-sort-revert
(unless full-search mu4e-search-results-limit))
;;; when we're starting a new search, we also kill the ;;; when we're starting a new search, we also kill the
;;; view window, if any ;;; view window, if any
(ignore-errors (delete-window mu4e~headers-view-win)))) (ignore-errors (delete-window mu4e~headers-view-win))))
@ -765,7 +825,7 @@ limited to the message at point and its descendants."
"Stack of queries before the present one.") "Stack of queries before the present one.")
(defvar mu4e~headers-query-future nil (defvar mu4e~headers-query-future nil
"Stack of queries after the present one.") "Stack of queries after the present one.")
(defvar mu4e~headers-query-present nil (defvar mu4e~headers-last-query nil
"The present (most recent) query.") "The present (most recent) query.")
(defvar mu4e~headers-query-stack-size 20 (defvar mu4e~headers-query-stack-size 20
"Maximum size for the query stacks.") "Maximum size for the query stacks.")
@ -807,9 +867,6 @@ to get it from; it's a symbol, either 'future or 'past."
;;; interactive functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; interactive functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defvar mu4e~headers-search-hist nil (defvar mu4e~headers-search-hist nil
@ -846,7 +903,7 @@ IGNORE-HISTORY is true, do *not* update the query history stack."
is non-nil, retrieve *all* results, otherwise only get up to is non-nil, retrieve *all* results, otherwise only get up to
`mu4e-search-results-limit'." `mu4e-search-results-limit'."
(interactive "P") (interactive "P")
(mu4e-headers-search mu4e~headers-query-present search-all nil t)) (mu4e-headers-search mu4e~headers-last-query search-all nil t))
(defun mu4e-headers-search-bookmark (&optional expr search-all edit) (defun mu4e-headers-search-bookmark (&optional expr search-all edit)
@ -878,10 +935,35 @@ non-nil, retrieve *all* results, otherwise only get up to
nil 'mu4e~headers-search-hist nil t)) nil 'mu4e~headers-search-hist nil t))
(search-all current-prefix-arg)) (search-all current-prefix-arg))
(list filter search-all))) (list filter search-all)))
(unless mu4e~headers-query-present (unless mu4e~headers-last-query
(error "There's nothing to filter")) (error "There's nothing to filter"))
(mu4e-headers-search (mu4e-headers-search
(format "(%s) AND %s" mu4e~headers-query-present filter) search-all)) (format "(%s) AND %s" mu4e~headers-last-query filter) search-all))
(defun mu4e-headers-change-sorting (&optional rerun)
"Interactively change the sorting/threading parameters. With prefix-argument,
rerun the last search with the new parameters."
(interactive "P")
(let* ((sortfield
(mu4e-read-option "Sortfield: " mu4e~headers-sortfield-choices))
(revert
(mu4e-read-option "Direction: "
'(("ascending" nil nil) ("descending" nil t)))))
(setq
mu4e-headers-sortfield sortfield
mu4e-headers-sort-revert revert)
(when rerun
(mu4e-headers-rerun-search))))
(defun mu4e-headers-toggle-threading (&optional rerun)
"Toggle threading on/off for the search results. With prefix-argument,
rerun the last search with the new setting for threading."
(interactive "P")
(setq mu4e-headers-show-threads (not mu4e-headers-show-threads))
(when rerun
(mu4e-headers-rerun-search)))
(defun mu4e-headers-view-message () (defun mu4e-headers-view-message ()
"View message at point. If there's an existing window for the "View message at point. If there's an existing window for the
@ -915,12 +997,12 @@ current window. "
(kill-buffer buf))) (kill-buffer buf)))
(mu4e~main-view)) (mu4e~main-view))
(defun mu4e-headers-rerun-search (full-search) (defun mu4e-headers-rerun-search ()
"Rerun the search for the last search expression; if none exists, "Rerun the search for the last search expression."
do a new search. If full-search is non-nil, return /all/ search (interactive)
results, otherwise show up to `mu4e-search-results-limit'." (mu4e-headers-search
(interactive "P") mu4e~headers-last-query
(mu4e-headers-search mu4e~headers-query-present full-search)) mu4e~headers-full-search))
(defun mu4e~headers-query-navigate (full-search whence) (defun mu4e~headers-query-navigate (full-search whence)
"Execute the previous query from the query stacks. WHENCE "Execute the previous query from the query stacks. WHENCE
@ -928,7 +1010,7 @@ determines where the query is taken from and is a symbol, either
`future' or `past'." `future' or `past'."
(let ((query (mu4e~headers-pop-query whence)) (let ((query (mu4e~headers-pop-query whence))
(where (if (eq whence 'future) 'past 'future))) (where (if (eq whence 'future) 'past 'future)))
(mu4e~headers-push-query mu4e~headers-query-present where) (mu4e~headers-push-query mu4e~headers-last-query where)
(mu4e-headers-search query full-search nil nil t))) (mu4e-headers-search query full-search nil nil t)))
(defun mu4e-headers-query-next (full-search) (defun mu4e-headers-query-next (full-search)
@ -967,8 +1049,8 @@ docid. Otherwise, return nil."
(mu4e~headers-highlight docid) (mu4e~headers-highlight docid)
;; update message view if it was already showing ;; update message view if it was already showing
(when (window-live-p mu4e~headers-view-win) (when (window-live-p mu4e~headers-view-win)
(mu4e-headers-view-message)) (mu4e-headers-view-message))
docid))) docid)))
(defun mu4e-headers-next (&optional n) (defun mu4e-headers-next (&optional n)
"Move point to the next message header. If this succeeds, return "Move point to the next message header. If this succeeds, return

View File

@ -299,19 +299,26 @@ terminates."
(defun mu4e~proc-remove (docid) (defun mu4e~proc-remove (docid)
"Remove message identified by docid. "Remove message identified by docid.
The results are reporter through either (:update ... ) or (:error The results are reporter through either (:update ... ) or (:error)
) sexp, which are handled my `mu4e-error-func', respectively." sexp, which are handled my `mu4e-error-func', respectively."
(mu4e~proc-send-command "remove docid:%d" docid)) (mu4e~proc-send-command "remove docid:%d" docid))
(defun mu4e~proc-find (query &optional maxnum) (defun mu4e~proc-find (query threads sortfield revert maxnum)
"Start a database query for QUERY, (optionally) getting up to "Start a database query for QUERY. If THREADS is non-nil, show
MAXNUM results. For each result found, a function is called, results in threaded fasion, SORTFIELD is a symmbol describing the
depending on the kind of result. The variables `mu4e-error-func' field to sort by (or nil); see `mu4e~headers-sortfield-choices'. If
contain the function that will be called for, resp., a REVERT is non-nil, sort Z->A instead of A->Z. MAXNUM determines the
message (header row) or an error." maximum number of results to return, or nil for 'unlimited'. For
each result found, a function is called, depending on the kind of
result. The variables `mu4e-error-func' contain the function that
will be called for, resp., a message (header row) or an error."
(mu4e~proc-send-command (mu4e~proc-send-command
"find query:\"%s\"%s" query "find query:\"%s\" threads:%s sortfield:%s reverse:%s maxnum:%d"
(if maxnum (format " maxnum:%d" maxnum) ""))) query
(if threads "true" "false")
(format "%S" sortfield)
(if revert "true" "false")
(if maxnum maxnum -1)))
(defun mu4e~proc-move (docid-or-msgid &optional maildir flags) (defun mu4e~proc-move (docid-or-msgid &optional maildir flags)
"Move message identified by DOCID-OR-MSGID. At least one of "Move message identified by DOCID-OR-MSGID. At least one of

View File

@ -30,7 +30,6 @@
(require 'mu4e-vars) (require 'mu4e-vars)
(require 'doc-view) (require 'doc-view)
(defcustom mu4e-html2text-command nil (defcustom mu4e-html2text-command nil
"Shell command that converts HTML from stdin into plain text on "Shell command that converts HTML from stdin into plain text on
stdout. If this is not defined, the emacs `html2text' tool will be stdout. If this is not defined, the emacs `html2text' tool will be
@ -433,7 +432,6 @@ A message plist looks something like:
\"6BDC23465F79238203498230942D81EE81AF0114E4E74@123213.mail.example.com\") \"6BDC23465F79238203498230942D81EE81AF0114E4E74@123213.mail.example.com\")
:in-reply-to \"6BDC23465F79238203498230942D81EE81AF0114E4E74@123213.mail.example.com\" :in-reply-to \"6BDC23465F79238203498230942D81EE81AF0114E4E74@123213.mail.example.com\"
:body-txt \"Hi Tom, ...\" :body-txt \"Hi Tom, ...\"
\)).
\)). \)).
Some notes on the format: Some notes on the format:
- The address fields are lists of pairs (NAME . EMAIL), where NAME can be nil. - The address fields are lists of pairs (NAME . EMAIL), where NAME can be nil.
@ -470,7 +468,7 @@ point in eiter the headers buffer or the view buffer."
(defun mu4e-last-query () (defun mu4e-last-query ()
"Get the most recent query or nil if there is none." "Get the most recent query or nil if there is none."
(when (buffer-live-p mu4e~headers-buffer) (when (buffer-live-p mu4e~headers-buffer)
(with-current-buffer mu4e~headers-buffer (with-current-buffer mu4e~headers-buffer
mu4e~headers-last-query))) mu4e~headers-last-query)))
(defun mu4e-select-other-view () (defun mu4e-select-other-view ()
@ -790,6 +788,5 @@ is ignored."
(when img (when img
(newline) (newline)
(insert-image img imgpath nil t)))) (insert-image img imgpath nil t))))
(provide 'mu4e-utils) (provide 'mu4e-utils)

View File

@ -542,6 +542,9 @@ j jump to maildir
M-left previous query M-left previous query
M-right next query M-right next query
O change sort order
P toggle threading
a execute some action on header a execute some action on header
d mark for moving to the trash folder d mark for moving to the trash folder
@ -596,6 +599,34 @@ behavior can be influenced with the variable
For more information about marking, @xref{Marking}. For more information about marking, @xref{Marking}.
@subsection Sort order and threading
@anchor{Sort order and threading}
By default, @t{mu4e} sorts messages by date, in descending order: the most
recent messages are at the top. In addition, the messages are @emph{threaded},
i.e., shown in the context of a message thread; this also affects the sort
order.
You can change the sort order with @t{M-x mu4e-headers-change-sorting} or
@key{O}, and you can toggle threading on/off using @t{M-x
mu4e-headers-toggle-threading}. For both of these functions, if you provide a
prefix argument (@key{C-u}), the current search is updated immediately using
the new parameters.
If you want to change the defaults for these settings, you can use the
variables @code{mu4e-headers-sortfield} and @code{mu4e-headers-show-threads}.
Note that you can see the current settings in the emacs modeline; it shows the
current query, followed by the shortcut character for sortfield (the same
character you'd use in @code{mu4e-headers-change-sorting}. The next character
is either @t{a} (for ascending, A->Z order), or @t{d} (for descending, Z->A
order). If threading is enabled, the next character is a @t{T}, and finally,
if we're doing an unlimited, full search, the last character is an @t{F}.
So, suppose our query is @t{subject:foo maildir:/bar}, we're sorting by
subject in ascending order with threads enabled, and it's a full search. The
mode string will then look like: @t{subject:foo maildir:/bar(saTF)}.
@subsection Actions @subsection Actions
@code{mu4e-headers-action} (@key{a}) lets you pick some custom action to perform @code{mu4e-headers-action} (@key{a}) lets you pick some custom action to perform
@ -765,7 +796,7 @@ variable @code{mu4e-attachment-dir}, for example:
If you want to extract multiple attachments at once, you can do so by If you want to extract multiple attachments at once, you can do so by
prefixing the extracting command by @key{C-u}; so @key{C-u e} will ask you for prefixing the extracting command by @key{C-u}; so @key{C-u e} will ask you for
a range of attachments to extract (for example, 1 3-6 8). Range @t{a} is a a range of attachments to extract (for example, 1 3-6 8). Range @t{a} is a
shortcut for @emph{all} attachments. shortcut for @emph{all} attachments.
@subsection Viewing images inline @subsection Viewing images inline
@anchor{Viewing images inline} @anchor{Viewing images inline}
@ -1411,7 +1442,7 @@ tables, mathematical formulae etc. In addition, it can convert them to
An @emph{experimental} @t{mu4e} feature lets you edit your messages with An @emph{experimental} @t{mu4e} feature lets you edit your messages with
@t{org-mode}, and (optionally) convert them on the fly (when sending them) to @t{org-mode}, and (optionally) convert them on the fly (when sending them) to
messages with an HTML-part containing the rich-text version of your messages. messages with an HTML-part containing the rich-text version of your messages.
To enable all this, make sure you have To enable all this, make sure you have
@lisp @lisp
(require 'org-mu4e) (require 'org-mu4e)
@ -1578,7 +1609,7 @@ configuration:
(setq gnus-dired-mail-mode 'mu4e-user-agent) (setq gnus-dired-mail-mode 'mu4e-user-agent)
(add-hook 'dired-mode-hook 'turn-on-gnus-dired-mode) (add-hook 'dired-mode-hook 'turn-on-gnus-dired-mode)
@end lisp @end lisp
Then, mark the file(s) in @t{dired} you would like to attach and press @t{C-c Then, mark the file(s) in @t{dired} you would like to attach and press @t{C-c
RET C-a}, and you'll be asked whether to attach them to an existing message, RET C-a}, and you'll be asked whether to attach them to an existing message,
or create a new one. or create a new one.
@ -1862,7 +1893,7 @@ up-to-a-limited-number matches. Same for the other search based commands,
@code{mu4e-headers-search-bookmark} (default: @key{b}). @code{mu4e-headers-search-bookmark} (default: @key{b}).
@item @emph{How can I show attached images in my message view buffers?} See @item @emph{How can I show attached images in my message view buffers?} See
@ref{Viewing images inline}. @ref{Viewing images inline}.
@item @emph{How can I easily include attachments in the messages I write?} @item @emph{How can I easily include attachments in the messages I write?}
You can drag-and-drop from your desktop; alternatively, you can use @t{dired} You can drag-and-drop from your desktop; alternatively, you can use @t{dired}
-- see @ref{Attaching files with dired}. -- see @ref{Attaching files with dired}.
@item @emph{@t{mu4e} seems to remove myself from the Cc: list; how can I @item @emph{@t{mu4e} seems to remove myself from the Cc: list; how can I

View File

@ -288,8 +288,7 @@ mu_msg_field_id_from_name (const char* str, gboolean err)
for (i = 0; i != G_N_ELEMENTS(FIELD_DATA); ++i) for (i = 0; i != G_N_ELEMENTS(FIELD_DATA); ++i)
if (g_strcmp0(str, FIELD_DATA[i]._name) == 0) if (g_strcmp0(str, FIELD_DATA[i]._name) == 0)
return FIELD_DATA[i]._id; return FIELD_DATA[i]._id;
if (err)
if (err)
g_return_val_if_reached (MU_MSG_FIELD_ID_NONE); g_return_val_if_reached (MU_MSG_FIELD_ID_NONE);
return MU_MSG_FIELD_ID_NONE; return MU_MSG_FIELD_ID_NONE;

View File

@ -323,7 +323,8 @@ mu_query_run (MuQuery *self, const char* searchexpr, gboolean threads,
Xapian::Enquire enq (self->db()); Xapian::Enquire enq (self->db());
/* note, when our result will be *threaded*, we sort /* note, when our result will be *threaded*, we sort
* there, and don't let Xapian do any sorting */ * in our threading code (mu-threader etc.), and don't
* let Xapian do any sorting */
if (!threads && sortfieldid != MU_MSG_FIELD_ID_NONE) if (!threads && sortfieldid != MU_MSG_FIELD_ID_NONE)
enq.set_sort_by_value ((Xapian::valueno)sortfieldid, enq.set_sort_by_value ((Xapian::valueno)sortfieldid,
revert ? true : false); revert ? true : false);

View File

@ -119,8 +119,17 @@ to a shell command.
Using the \fBfind\fR command we can search for messages. Using the \fBfind\fR command we can search for messages.
.nf .nf
-> find query:"<query>" [maxnum:<maxnum>] -> find query:"<query>" [threads:true|false] [sortfield:<sortfield>]
[reverse:true|false] [maxnum:<maxnum>]
.fi .fi
The \fBquery\fR-parameter provides the search query; the
\fBthreads\fR-parameter determines whether the results will be returned in
threaded fashion or not; the \fBsortfield\fR-parameter (a string, "to",
"from", "subject", "date", "size", "prio") sets the search field, the
\fBreverse\fR-parameter, if true, set the sorting order Z->A and, finally, the
\fBmaxnum\fR-parameter limits the number of results to return (<= 0
means 'unlimited').
First, this will return an 'erase'-sexp, to clear the buffer from possible First, this will return an 'erase'-sexp, to clear the buffer from possible
results from a previous query. results from a previous query.
.nf .nf

View File

@ -517,23 +517,23 @@ cmd_compose (MuStore *store, MuQuery *query, GSList *args, GError **err)
static unsigned static unsigned
print_sexps (MuMsgIter *iter, int maxnum) print_sexps (MuMsgIter *iter, gboolean threads)
{ {
unsigned u, max; unsigned u;
u = 0; u = 0;
max = (maxnum > 0) ? (unsigned)maxnum : G_MAXUINT32;
while (!mu_msg_iter_is_done (iter) && u < max && !MU_TERMINATE) { while (!mu_msg_iter_is_done (iter) && !MU_TERMINATE) {
MuMsg *msg; MuMsg *msg;
msg = mu_msg_iter_get_msg_floating (iter); msg = mu_msg_iter_get_msg_floating (iter);
if (mu_msg_is_readable (msg)) { if (mu_msg_is_readable (msg)) {
char *sexp; char *sexp;
const MuMsgIterThreadInfo* ti;
ti = threads ? mu_msg_iter_get_thread_info (iter) : NULL;
sexp = mu_msg_to_sexp (msg, mu_msg_iter_get_docid (iter), sexp = mu_msg_to_sexp (msg, mu_msg_iter_get_docid (iter),
mu_msg_iter_get_thread_info (iter), ti, TRUE, FALSE);
TRUE, FALSE);
print_expr ("%s", sexp); print_expr ("%s", sexp);
g_free (sexp); g_free (sexp);
++u; ++u;
@ -686,6 +686,37 @@ cmd_extract (MuStore *store, MuQuery *query, GSList *args, GError **err)
return MU_OK; return MU_OK;
} }
/* parse the find parameters, and return the values as out params */
static MuError
get_find_params (GSList *args, gboolean *threads, MuMsgFieldId *sortfield,
gboolean *reverse, int *maxnum, GError **err)
{
const char *maxnumstr, *sortfieldstr;
/* maximum number of results */
maxnumstr = get_string_from_args (args, "maxnum", TRUE, NULL);
*maxnum = maxnumstr ? atoi (maxnumstr) : 0;
/* whether to show threads or not */
*threads = get_bool_from_args (args, "threads", TRUE, NULL);
/* field to sort by */
sortfieldstr = get_string_from_args (args, "sortfield", TRUE, NULL);
if (sortfieldstr) {
*sortfield = mu_msg_field_id_from_name (sortfieldstr, FALSE);
/* note: shortcuts are not allowed here */
if (*sortfield == MU_MSG_FIELD_ID_NONE) {
g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_IN_PARAMETERS,
"not a valid sort field: '%s'\n", sortfield);
return MU_G_ERROR_CODE(err);
}
} else
*sortfield = MU_MSG_FIELD_ID_DATE;
*reverse = get_bool_from_args (args, "reverse", TRUE, NULL);
return MU_OK;
}
/* /*
@ -701,21 +732,21 @@ static MuError
cmd_find (MuStore *store, MuQuery *query, GSList *args, GError **err) cmd_find (MuStore *store, MuQuery *query, GSList *args, GError **err)
{ {
MuMsgIter *iter; MuMsgIter *iter;
int maxnum;
unsigned foundnum; unsigned foundnum;
const char *querystr, *maxnumstr; int maxnum;
gboolean threads, reverse;
MuMsgFieldId sortfield;
const char *querystr;
GET_STRING_OR_ERROR_RETURN (args, "query", &querystr, err); GET_STRING_OR_ERROR_RETURN (args, "query", &querystr, err);
/* optional */ if (get_find_params (args, &threads, &sortfield,
maxnumstr = get_string_from_args (args, "maxnum", TRUE, NULL); &reverse, &maxnum, err) != MU_OK) {
maxnum = maxnumstr ? atoi (maxnumstr) : 0; print_and_clear_g_error (err);
return MU_OK;
}
/* TODO: ask for *all* results, then, get the <maxnum> newest iter = mu_query_run (query, querystr, threads, sortfield, reverse,
* ones; it seems we cannot get a sorted list of a subset of maxnum, err);
* the result --> needs investigation, this is a
* work-around */
iter = mu_query_run (query, querystr, TRUE,
MU_MSG_FIELD_ID_DATE, TRUE, -1, err);
if (!iter) { if (!iter) {
print_and_clear_g_error (err); print_and_clear_g_error (err);
return MU_OK; return MU_OK;
@ -723,12 +754,11 @@ cmd_find (MuStore *store, MuQuery *query, GSList *args, GError **err)
/* before sending new results, send an 'erase' message, so the /* before sending new results, send an 'erase' message, so the
* frontend knows it should erase the headers buffer. this * frontend knows it should erase the headers buffer. this
* will ensure that the output of two finds quickly will not * will ensure that the output of two finds will not be
* be mixed. */ * mixed. */
print_expr ("(:erase t)"); print_expr ("(:erase t)");
foundnum = print_sexps (iter, maxnum); foundnum = print_sexps (iter, threads);
print_expr ("(:found %u)", foundnum); print_expr ("(:found %u)", foundnum);
mu_msg_iter_destroy (iter); mu_msg_iter_destroy (iter);
return MU_OK; return MU_OK;