mirror of https://github.com/djcb/mu.git
* merge branch 'master' of github.com:djcb/mu
This commit is contained in:
commit
fa514435ea
|
@ -28,7 +28,7 @@ Documentation License.''
|
|||
|
||||
@titlepage
|
||||
@title @t{mu-guile} - extending @t{mu} with Guile Scheme
|
||||
@subtitle{version @value{mu-version}}
|
||||
@subtitle version @value{mu-version}
|
||||
@author Dirk-Jan C. Binnema
|
||||
|
||||
@c The following two commands start the copyright page.
|
||||
|
@ -59,7 +59,7 @@ Welcome to @t{mu-guile}!
|
|||
@t{mu} is a program for indexing and searching your e-mails. It can search
|
||||
your messages in many different ways, but sometimes that may not be
|
||||
enough. If you have very specific queries, or want do generate some
|
||||
statistics, you need some more power.
|
||||
statistics, you need some more power.
|
||||
|
||||
@t{mu-guile} is made for those cases. @t{mu-guile} exposes the internals of
|
||||
@t{mu} and its database to the @t{guile} programming language. Guile is the
|
||||
|
@ -80,6 +80,7 @@ Trust me, it's not very hard -- and it it's @emph{fun}!
|
|||
* Attachments and other parts::
|
||||
* Statistics::
|
||||
* Plotting data::
|
||||
* Writing scripts::
|
||||
|
||||
Appendices
|
||||
|
||||
|
@ -94,21 +95,22 @@ Appendices
|
|||
* Making sure it works::
|
||||
@end menu
|
||||
|
||||
This chapter walks you through the installation and the basic setup.
|
||||
This chapter walks you through the installation and the basic setup.
|
||||
|
||||
@node Installation
|
||||
@section Installation
|
||||
|
||||
@t{mu-guile} is part of @t{mu} - by installing the latter, the former is
|
||||
installed as well. At the time of writing, there are no distribution-provided
|
||||
packaged versions of @t{mu-guile}; so for now, you need to follow the steps
|
||||
below.
|
||||
necessarily installed as well. At the time of writing, there are no
|
||||
distribution-provided packaged versions of @t{mu-guile}; so for now, you need
|
||||
to follow the steps below.
|
||||
|
||||
@subsection Guile 2.x
|
||||
|
||||
@t{mu-guile} is built automatically when @t{mu} is built, if you have
|
||||
@t{guile} installed (@t{mu} checks for this during @t{configure}). Thus, the
|
||||
first step is to ensure you have @t{guile} installed.
|
||||
@t{guile} version 2 or higher. (@t{mu} checks for this during
|
||||
@t{configure}). Thus, the first step is to ensure you have @t{guile}
|
||||
installed.
|
||||
|
||||
On Debian/Ubuntu you can install @t{guile} 2.x using the @t{guile-2.0-dev}
|
||||
package (and its dependencies):
|
||||
|
@ -116,7 +118,7 @@ package (and its dependencies):
|
|||
$ sudo apt-get install guile-2.0-dev
|
||||
@end example
|
||||
|
||||
At the time of write, there are no official packages for
|
||||
At the time of writing, there are no official packages for
|
||||
Fedora@footnote{@url{https://bugzilla.redhat.com/show_bug.cgi?id=678238}}. If
|
||||
you are using Fedora or any other system that does not have packages, you need
|
||||
to compile @t{guile} from
|
||||
|
@ -139,9 +141,6 @@ $ sudo yum install gnuplot
|
|||
|
||||
@subsection mu
|
||||
|
||||
At the time of writing, there are no distribution packages for @t{mu-guile},
|
||||
so we are assuming installation from source packages.
|
||||
|
||||
Assuming @t{guile} 2.x is installed correctly, @t{mu} finds it during its
|
||||
@t{configure}-stage, and creates @t{mu-guile}. Building @t{mu} follows the
|
||||
normal steps -- please see the @t{mu} documentation for the details.
|
||||
|
@ -227,7 +226,7 @@ Now, copy-paste the following after the prompt:
|
|||
|
||||
@noindent
|
||||
After pressing @key{Enter}, you should get a list of all subjects of messages
|
||||
that match @t{hello}:
|
||||
that match @t{hello}:
|
||||
|
||||
@verbatim
|
||||
...
|
||||
|
@ -240,7 +239,7 @@ Subject: When all is lost
|
|||
|
||||
@noindent
|
||||
If all this works, congratulations! @t{mu-guile} is installed now, ready to
|
||||
serve your every whim!
|
||||
serve your every searching need!
|
||||
|
||||
@node Initializing mu-guile
|
||||
@chapter Initializing mu-guile
|
||||
|
@ -269,7 +268,7 @@ This program is free software, and you are welcome to redistribute it
|
|||
under certain conditions; type `,show c' for details.
|
||||
|
||||
Enter `,help' for help.
|
||||
scheme@(guile-user)>
|
||||
scheme@(guile-user)>
|
||||
@end verbatim
|
||||
@end cartouche
|
||||
|
||||
|
@ -293,7 +292,7 @@ scheme@(guile-user)> (mu:initialize)
|
|||
This opens the database for reading, using the default location of
|
||||
@file{~/.mu}@footnote{If you keep your @t{mu} database in a non-standard
|
||||
place, use @code{(mu:initialize "/path/to/my/mu/")}}
|
||||
|
||||
|
||||
Now, @t{mu-guile} is ready to go. In the next chapter, we go through the
|
||||
modules and show what you can do with them.
|
||||
|
||||
|
@ -311,15 +310,15 @@ In this chapter, we discuss searching messages and doing things with them.
|
|||
@node Finding messages
|
||||
@section Finding messages
|
||||
Now we are ready to retrieve some messages from the system. There are two main
|
||||
functions to do this:
|
||||
procedures to do this:
|
||||
|
||||
@itemize
|
||||
@item @code{(mu:message-list [<search-expression>])}
|
||||
@item @code{(mu:for-each-message <function> [<search-expression>])}
|
||||
@item @code{(mu:for-each-message <procedure> [<search-expression>])}
|
||||
@end itemize
|
||||
|
||||
@noindent
|
||||
The first function, @code{mu:message-list} returns a list of all messages
|
||||
The first procedure, @code{mu:message-list} returns a list of all messages
|
||||
matching @t{<search-expression>}; if you leave @t{<search-expression>} out, it
|
||||
returns @emph{all} messages. For example, to get all messages with @t{coffee}
|
||||
in the subject line:
|
||||
|
@ -333,7 +332,7 @@ $1 = (#<<mu:message> 9040640> #<<mu:message> 9040630>
|
|||
@noindent
|
||||
Apparently, we have three messages matching @t{subject:coffee}, so we get a
|
||||
list of three @code{<mu:message>} objects. Let's just use the
|
||||
@code{mu:subject} function ('method') provided by @code{<mu:message>} objects
|
||||
@code{mu:subject} procedure ('method') provided by @code{<mu:message>} objects
|
||||
to retrieve the subject-field (more about methods in the next section).
|
||||
|
||||
For your convenience, @t{guile} has saved the result of our last query in a
|
||||
|
@ -346,8 +345,8 @@ $2 = "Re: best coffee ever!"
|
|||
@end verbatim
|
||||
|
||||
@noindent
|
||||
The second function we mentioned, @code{mu:for-each-message}, executes some
|
||||
function for each message matched by the search expression (or @emph{all}
|
||||
The second procedure we mentioned, @code{mu:for-each-message}, executes some
|
||||
procedure for each message matched by the search expression (or @emph{all}
|
||||
messages if the search expression is omitted):
|
||||
|
||||
@verbatim
|
||||
|
@ -380,7 +379,7 @@ Now that we've seen how to retrieve lists of message objects
|
|||
|
||||
@code{<mu:message>} defines the following methods that all take a single
|
||||
@code{<mu:message>} object as a parameter. We won't go into the exact meanings
|
||||
for all of these functions here - for the details about various flags /
|
||||
for all of these procedures here - for the details about various flags /
|
||||
properties, please refer to the @t{mu-find} man-page.
|
||||
|
||||
@itemize
|
||||
|
@ -472,19 +471,19 @@ e-mail corpus.
|
|||
|
||||
We can retrieve the sender and recipients of an e-mail message using methods
|
||||
like @code{mu:from}, @code{mu:to} etc.; @xref{Message methods}. These
|
||||
functions return the list of recipients as a single string; however, often it
|
||||
procedures return the list of recipients as a single string; however, often it
|
||||
is more useful to deal with recipients as separate objects.
|
||||
|
||||
@menu
|
||||
* Contact functions and objects::
|
||||
* Contact procedures and objects::
|
||||
* All contacts::
|
||||
* Utility functions::
|
||||
* Utility procedures::
|
||||
* Example - mutt export::
|
||||
@end menu
|
||||
|
||||
|
||||
@node Contact functions and objects
|
||||
@section Contact functions and objects
|
||||
@node Contact procedures and objects
|
||||
@section Contact procedures and objects
|
||||
|
||||
Message objects (@pxref{Messages}) have a method @t{mu:contacts}:
|
||||
|
||||
|
@ -529,17 +528,17 @@ Sometimes you may want to inspect @emph{all} the different contacts in the
|
|||
@t{mu} database. This is useful, for instance, when exporting contacts to some
|
||||
external format that can then be important in an e-mail program.
|
||||
|
||||
To enable this, there is the function @code{mu:for-each-contact}, defined as
|
||||
To enable this, there is the procedure @code{mu:for-each-contact}, defined as
|
||||
|
||||
@code{(mu:for-each-contact function [search-expression])}.
|
||||
@code{(mu:for-each-contact procedure [search-expression])}.
|
||||
|
||||
This will aggregate the unique contacts from @emph{all} messages matching
|
||||
@t{<search-expression>} (when it is left empty, it will match all messages in
|
||||
the database), and execute @t{function} for each of them.
|
||||
the database), and execute @t{procedure} for each of them.
|
||||
|
||||
The @t{function} receives an object of the type @t{<mu:contact-with-stats>},
|
||||
The @t{procedure} receives an object of the type @t{<mu:contact-with-stats>},
|
||||
which is a @emph{subclass} of the @t{<mu:contact>} class discussed in
|
||||
@xref{Contact functions and objects}. @t{<mu:contact-with-stats>} objects
|
||||
@xref{Contact procedures and objects}. @t{<mu:contact-with-stats>} objects
|
||||
expose the following additional methods:
|
||||
|
||||
@itemize
|
||||
|
@ -553,11 +552,11 @@ The method assumes an e-mail address is unique for a certain contact; if a
|
|||
certain e-mail address occurs with different names, it uses the most recent
|
||||
non-empty name.
|
||||
|
||||
@node Utility functions
|
||||
@section Utility functions
|
||||
@node Utility procedures
|
||||
@section Utility procedures
|
||||
|
||||
To make dealing with contacts even easier, there are a number of utility
|
||||
functions that can save you a bit of typing.
|
||||
procedures that can save you a bit of typing.
|
||||
|
||||
For converting contacts to some textual form, there is @code{(mu:contact->string
|
||||
<mu:contact> format)}, which takes a contact and returns a text string with
|
||||
|
@ -578,8 +577,8 @@ something like:
|
|||
alias <nick> [<name>] "<" <email> ">"
|
||||
@end verbatim
|
||||
|
||||
Anyway, there is the function @code{(mu:contact->string <mu:contact> format)}
|
||||
that we can use to do the conversion.
|
||||
@t{mu guile} provides the procedure @code{(mu:contact->string <mu:contact>
|
||||
format)} that we can use to do the conversion.
|
||||
|
||||
We may want to focus on people with whom we have frequent correspondence; so
|
||||
we may want to limit ourselves to people we have seen at least 10 times in the
|
||||
|
@ -654,7 +653,7 @@ if there is none.
|
|||
Then, we may want to save the part to a file; this can be done using either:
|
||||
@itemize
|
||||
@item @code{(mu:save part <part>)} - save a part to a temporary file, return the file
|
||||
name@footnote{the temporary filename is a predictable function of (user-id,
|
||||
name@footnote{the temporary filename is a predictable procedure of (user-id,
|
||||
msg-path, part-index)}
|
||||
@item @code{(mu:save-as <part> <path>)} - save part to file at path
|
||||
@end itemize
|
||||
|
@ -703,7 +702,7 @@ probably be a bit more elegant.
|
|||
@node Statistics
|
||||
@chapter Statistics
|
||||
|
||||
@t{mu-guile} offers some convenience functions to determine various statistics
|
||||
@t{mu-guile} offers some convenience procedures to determine various statistics
|
||||
about the messages in the database.
|
||||
|
||||
@menu
|
||||
|
@ -743,16 +742,16 @@ scheme@@(guile-user)> ;; subject length
|
|||
scheme@@(guile-user)> (mu:correl mu:size (lambda (msg)
|
||||
(string-length (mu:subject msg))) "subject:hello")
|
||||
$5 = -0.10804368622292
|
||||
scheme@@(guile-user)>
|
||||
scheme@@(guile-user)>
|
||||
@end example
|
||||
|
||||
@node Tabulating values
|
||||
@section Tabulating values
|
||||
|
||||
@code{(mu:tabulate <function> [<search-expr>])} applies @t{<function>} to each
|
||||
@code{(mu:tabulate <procedure> [<search-expr>])} applies @t{<procedure>} to each
|
||||
message matching @t{<search-expr>} (leave empty to match @emph{all} messages),
|
||||
and returns a associative list (a list of pairs) with each of the different
|
||||
results of @t{<function>} and their frequencies. For fields that contain lists
|
||||
results of @t{<procedure>} and their frequencies. For fields that contain lists
|
||||
of values (such as address-fields), each of the values in the list is added
|
||||
separately.
|
||||
|
||||
|
@ -785,7 +784,7 @@ exec guile -s $0 $@
|
|||
@end lisp
|
||||
|
||||
|
||||
The function @code{weekday-table} uses @code{mu:tabulate-message} to get the
|
||||
The procedure @code{weekday-table} uses @code{mu:tabulate-message} to get the
|
||||
frequencies per hour -- this returns a list of pairs:
|
||||
@verbatim
|
||||
((5 . 2339) (0 . 2278) (4 . 2800) (2 . 3184) (6 . 1856) (3 . 2833) (1 . 2993))
|
||||
|
@ -826,10 +825,10 @@ something like this:
|
|||
(sort
|
||||
(mu:tabulate mu:subject)
|
||||
(lambda (a b) (> (cdr a) (cdr b))))
|
||||
10)
|
||||
10)
|
||||
@end lisp
|
||||
|
||||
If this is not short enough, @t{mu-guile} offers a convenience function to do
|
||||
If this is not short enough, @t{mu-guile} offers a convenience procedure to do
|
||||
this: @code{mu:top-n-most-frequent}. For example, to get the top-10 people we
|
||||
sent mail to most often:
|
||||
|
||||
|
@ -848,7 +847,7 @@ You can plot the results in the format produced by @code{mu:tabulate} with the
|
|||
@t{gnuplot}@footnote{@url{http://www.gnuplot.info/}} program to be installed
|
||||
on your system.
|
||||
|
||||
The @code{mu:plot-histogram} function takes the following arguments:
|
||||
The @code{mu:plot-histogram} procedure takes the following arguments:
|
||||
|
||||
@code{(mu:plot-histogram <data> <title> <x-label> <y-label> [<want-ascii>])}
|
||||
|
||||
|
@ -908,6 +907,57 @@ Frequency
|
|||
@end verbatim
|
||||
@end cartouche
|
||||
|
||||
@node Writing scripts
|
||||
@chapter Writing scripts
|
||||
|
||||
The @t{mu} program has built-in support for running guile-scripts, and comes
|
||||
with a number of examples.
|
||||
|
||||
You can get a list of all scripts with the @t{mu script} command:
|
||||
@verbatim
|
||||
$ mu script
|
||||
Available scripts (use --verbose for details):
|
||||
* find-dups: find duplicate messages
|
||||
* msgs-count: count the number of messages matching some query
|
||||
* msgs-per-day: graph the number of messages per day
|
||||
* msgs-per-hour: graph the number of messages per hour
|
||||
* msgs-per-month: graph the number of messages per month
|
||||
* msgs-per-year: graph the number of messages per year
|
||||
* msgs-per-year-month: graph the number of messages per year-month
|
||||
@end verbatim
|
||||
|
||||
You can then execute such a script by its name:
|
||||
@verbatim
|
||||
$ mu msgs-per-month --textonly --query=hello
|
||||
|
||||
|
||||
Messages per month matching hello
|
||||
|
||||
240 ++-+-----+----+-----+-----+-----+----+-----+-----+-----+----+-----+-++
|
||||
| + + + + "/tmp/filewi9H0N" using 2:xticlabels(1) ****** |
|
||||
220 ++ * * ******
|
||||
| * * * *
|
||||
200 ++ * * * +*
|
||||
| * * * *
|
||||
180 ++ ****** * * * +*
|
||||
| * * * * * *
|
||||
160 ****** * * * * * +*
|
||||
* * * * * * * *
|
||||
* ******* * * * * ****** * *
|
||||
140 *+ ** * * * * * * ******** +*
|
||||
* ** ******* * * * * * ** ** *
|
||||
120 *+ ** ** ******* * * * * ** ** +*
|
||||
* ** ** ** * * * ******* ** ** *
|
||||
100 *+ ** ** ** * * * * ** ** ** +*
|
||||
* + ** + ** + ** + * + * + + * + * + ** + ** + ** + *
|
||||
80 **********************************************************************
|
||||
Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
|
||||
Month
|
||||
@end verbatim
|
||||
|
||||
Please refer to the @t{mu-script} man-page for some details on writing your
|
||||
own scripts.
|
||||
|
||||
@node GNU Free Documentation License
|
||||
@appendix GNU Free Documentation License
|
||||
|
||||
|
|
|
@ -277,7 +277,6 @@ either \"org-contact\", \"mutt-alias\", \"mutt-ab\",
|
|||
"\""))
|
||||
(else (error "Unsupported format")))))
|
||||
|
||||
|
||||
;; message parts
|
||||
|
||||
|
||||
|
|
|
@ -22,7 +22,8 @@ EXTRA_DIST= \
|
|||
msgs-per-hour.scm \
|
||||
msgs-per-month.scm \
|
||||
msgs-per-day.scm \
|
||||
msgs-per-year-month.scm
|
||||
msgs-per-year-month.scm \
|
||||
find-dups.scm
|
||||
|
||||
muguiledistscriptdir = $(pkgdatadir)/scripts/
|
||||
muguiledistscript_SCRIPTS = $(EXTRA_DIST)
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
#!/bin/sh
|
||||
exec guile -e main -s $0 $@
|
||||
!#
|
||||
;;
|
||||
;; Copyright (C) 2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
||||
;;
|
||||
;; This program 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, or (at your option) any
|
||||
;; later version.
|
||||
;;
|
||||
;; This program 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 program; if not, write to the Free Software Foundation,
|
||||
;; Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
;; INFO: find duplicate messages
|
||||
;; INFO: options:
|
||||
;; INFO: --muhome=<muhome>: path to mu home dir
|
||||
|
||||
(use-modules (mu) (mu script) (mu stats))
|
||||
(use-modules (ice-9 getopt-long) (ice-9 optargs)
|
||||
(ice-9 popen) (ice-9 format) (ice-9 rdelim))
|
||||
|
||||
(define (md5sum path)
|
||||
(let* ((port (open-pipe* OPEN_READ "md5sum" path))
|
||||
(md5 (read-delimited " " port)))
|
||||
(close-pipe port)
|
||||
md5))
|
||||
|
||||
|
||||
(define (find-dups)
|
||||
(let ((id-table (make-hash-table 20000)))
|
||||
;; fill the hash with <msgid-size> => <list of paths>
|
||||
(mu:for-each-message
|
||||
(lambda (msg)
|
||||
(let* ((id (format #f "~a-~d" (mu:message-id msg)
|
||||
(mu:size msg)))
|
||||
(lst (hash-ref id-table id)))
|
||||
(if lst
|
||||
(set! lst (cons (mu:path msg) lst))
|
||||
(set! lst (list (mu:path msg))))
|
||||
(hash-set! id-table id lst))))
|
||||
;; list all the paths with multiple elements; check the md5sum to
|
||||
;; make 100%-minus-ε sure they are really the same file.
|
||||
(hash-for-each
|
||||
(lambda (id paths)
|
||||
(if (> (length paths) 1)
|
||||
(let ((hash (make-hash-table 10)))
|
||||
(for-each
|
||||
(lambda (path)
|
||||
(let* ((md5 (md5sum path)) (lst (hash-ref hash md5)))
|
||||
(if lst
|
||||
(set! lst (cons path lst))
|
||||
(set! lst (list path)))
|
||||
(hash-set! hash md5 lst)))
|
||||
paths)
|
||||
;; hash now maps the md5sum to the messages...
|
||||
(hash-for-each
|
||||
(lambda (md5 mpaths)
|
||||
(if (> (length mpaths) 1)
|
||||
(begin
|
||||
(format #t "md5sum: ~a:\n" md5)
|
||||
(let ((num 1))
|
||||
(for-each
|
||||
(lambda (path)
|
||||
(format #t "\t~d. ~a\n" num path)
|
||||
(set! num (+ 1 num)))
|
||||
mpaths)))))
|
||||
hash))))
|
||||
id-table)))
|
||||
|
||||
|
||||
|
||||
(define (main args)
|
||||
"Run some statistics function.
|
||||
Interpret argument-list ARGS (like command-line
|
||||
arguments). Possible arguments are:
|
||||
--muhome (path to alternative mu home directory)."
|
||||
(setlocale LC_ALL "")
|
||||
(let* ((optionspec '( (muhome (value #t))
|
||||
(help (single-char #\h) (value #f))))
|
||||
(options (getopt-long args optionspec))
|
||||
(help (option-ref options 'help #f))
|
||||
(muhome (option-ref options 'muhome #f)))
|
||||
(mu:initialize muhome)
|
||||
(find-dups)))
|
||||
|
||||
|
||||
;; Local Variables:
|
||||
;; mode: scheme
|
||||
;; End:
|
|
@ -520,7 +520,7 @@ count_colons (const char *str)
|
|||
|
||||
|
||||
static MuMsgIterThreadInfo*
|
||||
thread_info_new (gchar *threadpath, gboolean root, gboolean child,
|
||||
thread_info_new (gchar *threadpath, gboolean root, gboolean first_child,
|
||||
gboolean empty_parent, gboolean has_child, gboolean is_dup)
|
||||
{
|
||||
MuMsgIterThreadInfo *ti;
|
||||
|
@ -529,9 +529,9 @@ thread_info_new (gchar *threadpath, gboolean root, gboolean child,
|
|||
ti->threadpath = threadpath;
|
||||
ti->level = count_colons (threadpath); /* hacky... */
|
||||
|
||||
ti->prop = 0;
|
||||
ti->prop = MU_MSG_ITER_THREAD_PROP_NONE;
|
||||
ti->prop |= root ? MU_MSG_ITER_THREAD_PROP_ROOT : 0;
|
||||
ti->prop |= child ? MU_MSG_ITER_THREAD_PROP_FIRST_CHILD : 0;
|
||||
ti->prop |= first_child ? MU_MSG_ITER_THREAD_PROP_FIRST_CHILD : 0;
|
||||
ti->prop |= empty_parent ? MU_MSG_ITER_THREAD_PROP_EMPTY_PARENT : 0;
|
||||
ti->prop |= is_dup ? MU_MSG_ITER_THREAD_PROP_DUP : 0;
|
||||
ti->prop |= has_child ? MU_MSG_ITER_THREAD_PROP_HAS_CHILD : 0;
|
||||
|
|
|
@ -159,6 +159,8 @@ gboolean mu_msg_iter_calculate_threads (MuMsgIter *iter);
|
|||
|
||||
|
||||
enum _MuMsgIterThreadProp {
|
||||
MU_MSG_ITER_THREAD_PROP_NONE = 0 << 0,
|
||||
|
||||
MU_MSG_ITER_THREAD_PROP_ROOT = 1 << 0,
|
||||
MU_MSG_ITER_THREAD_PROP_FIRST_CHILD = 1 << 1,
|
||||
MU_MSG_ITER_THREAD_PROP_EMPTY_PARENT = 1 << 2,
|
||||
|
|
|
@ -438,7 +438,6 @@ mu_util_locale_is_utf8 (void)
|
|||
gboolean
|
||||
mu_util_fputs_encoded (const char *str, FILE *stream)
|
||||
{
|
||||
char *conv;
|
||||
int rv;
|
||||
|
||||
g_return_val_if_fail (str, FALSE);
|
||||
|
@ -449,10 +448,14 @@ mu_util_fputs_encoded (const char *str, FILE *stream)
|
|||
rv = fputs (str, stream);
|
||||
else { /* charset is _not_ utf8, so we actually have to
|
||||
* convert it..*/
|
||||
GError *err;
|
||||
unsigned bytes;
|
||||
err = NULL;
|
||||
|
||||
GError *err;
|
||||
unsigned bytes;
|
||||
char *conv;
|
||||
|
||||
err = NULL;
|
||||
conv = g_locale_from_utf8 (str, -1, (gsize*)&bytes, NULL, &err);
|
||||
|
||||
if (!conv || err) {
|
||||
/* conversion failed; this happens because is
|
||||
* some cases GMime may gives us non-UTF-8
|
||||
|
@ -462,14 +465,15 @@ mu_util_fputs_encoded (const char *str, FILE *stream)
|
|||
g_warning ("%s: g_locale_from_utf8 failed: %s",
|
||||
__FUNCTION__,
|
||||
err ? err->message : "conversion failed");
|
||||
g_clear_error (&err);
|
||||
g_free (conv);
|
||||
conv = g_strescape (str, NULL);
|
||||
}
|
||||
g_clear_error (&err);
|
||||
|
||||
rv = fputs (conv, stream);
|
||||
g_free (conv);
|
||||
}
|
||||
|
||||
}
|
||||
return (rv == EOF) ? FALSE : TRUE;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.TH MU FIND 1 "December 2012" "User Manuals"
|
||||
.TH MU FIND 1 "June 2013" "User Manuals"
|
||||
|
||||
.SH NAME
|
||||
|
||||
|
@ -53,9 +53,9 @@ messages.
|
|||
|
||||
A wildcard search is a search where a \fB*\fR matches the last \fIn\fR
|
||||
character(s) in some string. The string must always start with one or more
|
||||
characters before the wildcards. Since version 0.9.6, \fBmu\fR also supports
|
||||
wildcard searches for all fields except maildirs and paths. So, to get all
|
||||
mails with a subject containing a word starting with \fBcom\fR, you can use:
|
||||
characters before the wildcard. \fBmu\fR supports wildcard searches for all
|
||||
fields except maildirs and paths. To get all mails with a subject containing a
|
||||
word starting with \fBcom\fR, you can use:
|
||||
|
||||
.nf
|
||||
$ mu find 'subject:com*'
|
||||
|
@ -68,9 +68,6 @@ remember that the '*' invokes the wildcard search only when used as the
|
|||
rightmost character of a search term. Furthermore, it is \fBnot\fR a regular
|
||||
expression.
|
||||
|
||||
In older versions of mu, queries were logged in \fI<mu-home>/mu.log\fR;
|
||||
however, since version 0.9, mu no longer does this.
|
||||
|
||||
The basic way to search a message is to type some words matching it, as you
|
||||
would do in an internet search engine. For example,
|
||||
|
||||
|
@ -656,7 +653,7 @@ non-zero return value, for example:
|
|||
Please report bugs if you find them:
|
||||
.BR http://code.google.com/p/mu0/issues/list
|
||||
If you have specific messages which are not matched correctly, please attach
|
||||
them (appropriately censored of course).
|
||||
them (appropriately censored if needed).
|
||||
|
||||
.SH AUTHOR
|
||||
|
||||
|
|
|
@ -1,32 +1,31 @@
|
|||
.TH MU SCRIPT 1 "March 2013" "User Manuals"
|
||||
.TH MU SCRIPT 1 "June 2013" "User Manuals"
|
||||
|
||||
.SH NAME
|
||||
|
||||
mu script\- run a mu script
|
||||
mu script\- show the available mu scripts, and run them.
|
||||
|
||||
.SH SYNOPSIS
|
||||
|
||||
.B mu script [options] [--script=<script>] [<pattern>] [-- [script-options]]
|
||||
.B mu script [options] [<pattern>]
|
||||
|
||||
.B mu <script-name> [<script-options>]
|
||||
|
||||
.SH DESCRIPTION
|
||||
|
||||
\fBmu script\fR is the \fBmu\fR command to list available \fBmu\fR scripts,
|
||||
and run them. The scripts are implemented in the Guile programming language,
|
||||
and thus only work if your \fBmu\fR is built with support for Guile. In
|
||||
\fBmu script\fR is the \fBmu\fR command to list available \fBmu\fR scripts.
|
||||
The scripts are to be implemented in the Guile programming language, and
|
||||
therefore only work if your \fBmu\fR is built with support for Guile. In
|
||||
addition, many scripts require you to have \fBgnuplot\fR installed.
|
||||
|
||||
Without any parameters, \fBmu script\fR lists the available scripts. If you
|
||||
provide a pattern (regular expression), only the scripts whose name or
|
||||
one-line description match this pattern, are listed. See the examples below.
|
||||
provide a pattern (a regular expression), only the scripts whose name or
|
||||
one-line description match this pattern are listed. See the examples below.
|
||||
|
||||
\fBmu\fR ships with a number of scripts.
|
||||
|
||||
.SH OPTIONS
|
||||
|
||||
.TP
|
||||
\fB\-\-script=\fR\fI<script>\fR
|
||||
run the given script.
|
||||
|
||||
\fB\-\-verbose\fR,\fB\-v\fR
|
||||
when listing the available scripts, show the long descriptions.
|
||||
|
||||
|
@ -45,10 +44,10 @@ List all available scripts matching \fImonth\fR (long descriptions):
|
|||
$ mu script -v month
|
||||
.fi
|
||||
|
||||
Run the \fImsgs-per-month\fR script, and pass it the \fI--textonly\fR
|
||||
parameter:
|
||||
Run the \fImsgs-per-month\fR script for messages matching 'hello', and pass it
|
||||
the \fI--textonly\fR parameter:
|
||||
.nf
|
||||
$ mu script --script=msgs-per-month -- --textonly
|
||||
$ mu msgs-per-month --query=hello --textonly
|
||||
.fi
|
||||
|
||||
.SH RETURN VALUE
|
||||
|
@ -60,8 +59,8 @@ code when this is not the case.
|
|||
|
||||
You can make your own Scheme scripts accessible through \fBmu script\fR by
|
||||
putting them in \fI<muhome>/scripts\fR (which is typically
|
||||
\fI~/.mu/scripts\fR). It is a good idea to document it using some special
|
||||
comments in the source code:
|
||||
\fI~/.mu/scripts\fR). It is a good idea to document the scripts by using some
|
||||
special comments in the source code:
|
||||
.nf
|
||||
;; INFO: this is my script -- one-line description
|
||||
;; INFO: (longer description)
|
||||
|
|
|
@ -177,9 +177,9 @@ mu_cmd_script (MuConfig *opts, GError **err)
|
|||
if (err && *err)
|
||||
goto leave;
|
||||
|
||||
if (!opts->script) {
|
||||
if (g_strcmp0 (opts->cmdstr, "script") == 0) {
|
||||
print_scripts (scripts, !opts->nocolor, opts->verbose,
|
||||
opts->params[1], err);
|
||||
opts->script_params[0], err);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
|
@ -191,8 +191,7 @@ mu_cmd_script (MuConfig *opts, GError **err)
|
|||
}
|
||||
|
||||
/* do it! */
|
||||
mu_script_guile_run (msi, opts->muhome,
|
||||
(const gchar**)&opts->params[1], err);
|
||||
mu_script_guile_run (msi, opts->muhome, opts->script_params, err);
|
||||
leave:
|
||||
/* this won't be reached, unless there is some error */
|
||||
mu_script_info_list_destroy (scripts);
|
||||
|
|
|
@ -367,7 +367,7 @@ mu_cmd_remove (MuStore *store, MuConfig *opts, GError **err)
|
|||
g_return_val_if_fail (opts->cmd == MU_CONFIG_CMD_REMOVE,
|
||||
MU_ERROR_INTERNAL);
|
||||
|
||||
/* note: params[0] will be 'add' */
|
||||
/* note: params[0] will be 'remove' */
|
||||
if (!opts->params[0] || !opts->params[1]) {
|
||||
g_warning ("usage: mu remove <file> [<files>]");
|
||||
mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS,
|
||||
|
@ -593,6 +593,8 @@ set_log_options (MuConfig *opts)
|
|||
logopts |= MU_LOG_OPTIONS_DEBUG;
|
||||
}
|
||||
|
||||
|
||||
|
||||
MuError
|
||||
mu_cmd_execute (MuConfig *opts, GError **err)
|
||||
{
|
||||
|
@ -627,10 +629,7 @@ mu_cmd_execute (MuConfig *opts, GError **err)
|
|||
case MU_CONFIG_CMD_SERVER:
|
||||
merr = with_store (mu_cmd_server, opts, FALSE, err); break;
|
||||
default:
|
||||
show_usage ();
|
||||
mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS,
|
||||
"unknown command '%s'", opts->cmdstr);
|
||||
return MU_ERROR_IN_PARAMETERS;
|
||||
merr = MU_ERROR_IN_PARAMETERS; break;
|
||||
}
|
||||
|
||||
return merr;
|
||||
|
|
|
@ -300,13 +300,14 @@ config_options_group_script (void)
|
|||
{
|
||||
GOptionGroup *og;
|
||||
GOptionEntry entries[] = {
|
||||
{"script", 0, 0, G_OPTION_ARG_STRING, &MU_CONFIG.script,
|
||||
"script to run (see `mu help script')", "<script>"},
|
||||
{NULL, 0, 0, 0, NULL, NULL, NULL}
|
||||
{G_OPTION_REMAINING, 0,0, G_OPTION_ARG_STRING_ARRAY,
|
||||
&MU_CONFIG.params, "script parameters", NULL},
|
||||
{NULL, 0, 0, 0, NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
og = g_option_group_new("script", "Options for the 'script' command",
|
||||
"", NULL, NULL);
|
||||
|
||||
g_option_group_add_entries(og, entries);
|
||||
|
||||
return og;
|
||||
|
@ -461,7 +462,7 @@ cmd_from_string (const char *str)
|
|||
{ "index", MU_CONFIG_CMD_INDEX },
|
||||
{ "mkdir", MU_CONFIG_CMD_MKDIR },
|
||||
{ "remove", MU_CONFIG_CMD_REMOVE },
|
||||
{ "script", MU_CONFIG_CMD_SCRIPT },
|
||||
{ "script", MU_CONFIG_CMD_SCRIPT },
|
||||
{ "server", MU_CONFIG_CMD_SERVER },
|
||||
{ "verify", MU_CONFIG_CMD_VERIFY },
|
||||
{ "view", MU_CONFIG_CMD_VIEW }
|
||||
|
@ -474,7 +475,8 @@ cmd_from_string (const char *str)
|
|||
if (strcmp (str, cmd_map[i].name) == 0)
|
||||
return cmd_map[i].cmd;
|
||||
|
||||
return MU_CONFIG_CMD_UNKNOWN;
|
||||
/* if we don't recognize it, it may be some script */
|
||||
return MU_CONFIG_CMD_SCRIPT;
|
||||
}
|
||||
|
||||
|
||||
|
@ -673,6 +675,17 @@ parse_params (int *argcp, char ***argvp, GError **err)
|
|||
rv = g_option_context_parse (context, argcp, argvp, err) &&
|
||||
cmd_help ();
|
||||
break;
|
||||
case MU_CONFIG_CMD_SCRIPT:
|
||||
/* all unknown commands are passed to 'script' */
|
||||
g_option_context_set_ignore_unknown_options (context, TRUE);
|
||||
group = get_option_group (MU_CONFIG.cmd);
|
||||
g_option_context_add_group (context, group);
|
||||
rv = g_option_context_parse (context, argcp, argvp, err);
|
||||
MU_CONFIG.script = g_strdup (MU_CONFIG.cmdstr);
|
||||
/* argvp contains the script parameters */
|
||||
MU_CONFIG.script_params = (const char**)&((*argvp)[1]);
|
||||
break;
|
||||
|
||||
default:
|
||||
group = get_option_group (MU_CONFIG.cmd);
|
||||
if (group)
|
||||
|
|
|
@ -183,6 +183,7 @@ struct _MuConfig {
|
|||
* (open) the attmnt using xdgopen */
|
||||
/* options for mu-script */
|
||||
gchar *script; /* script to run */
|
||||
const char **script_params; /* parameters for scripts */
|
||||
};
|
||||
typedef struct _MuConfig MuConfig;
|
||||
|
||||
|
|
|
@ -142,25 +142,21 @@ mu4e e-mail client.
|
|||
|
||||
#BEGIN MU_CONFIG_CMD_SCRIPT
|
||||
#STRING
|
||||
mu script [--script=<script>] [<pattern>] [-v] -- [script-options]
|
||||
mu script [<pattern>] [-v]
|
||||
mu <script-name> [<script options>]
|
||||
#STRING
|
||||
Without any parameter, list the available scripts. With <pattern>,
|
||||
list only those scripts whose name or one-line description matches it.
|
||||
With -v, give longer descriptions of each script.
|
||||
|
||||
With --script=<script>, run the script whose name is <script>; pass
|
||||
any arguments to the script after the '--' double-dash.
|
||||
List the available scripts and/or run them (if mu was built with support for
|
||||
scripts). With <pattern>, list only those scripts whose name or one-line
|
||||
description matches it. With -v, get a longer description for each script.
|
||||
|
||||
Some examples:
|
||||
List all available scripts (one-line descriptions):
|
||||
$ mu script
|
||||
|
||||
List all available scripts matching 'month' (long descriptions):
|
||||
$ mu script -v month
|
||||
|
||||
Run the 'msgs-per-month' script, and pass it the '--textonly' parameter:
|
||||
$ mu script --script=msgs-per-month -- --textonly
|
||||
(as mentioned, parameters to the script follow the '--')
|
||||
$ mu msgs-per-month --textonly
|
||||
#END
|
||||
|
||||
|
||||
|
|
|
@ -192,11 +192,16 @@ If needed, set the Fcc header, and register the handler function."
|
|||
|
||||
|
||||
|
||||
(defconst mu4e~compose-hidden-headers
|
||||
(defvar mu4e-compose-hidden-headers
|
||||
`("^References:" "^Face:" "^X-Face:"
|
||||
"^X-Draft-From:" "^User-agent:")
|
||||
"Hidden headers when composing.")
|
||||
|
||||
(defun mu4e~compose-hide-headers ()
|
||||
"Hide the headers as per `mu4e-compose-hidden-headers'."
|
||||
(let ((message-hidden-headers mu4e-compose-hidden-headers))
|
||||
(message-hide-headers)))
|
||||
|
||||
(defconst mu4e~compose-address-fields-regexp
|
||||
"^\\(To\\|B?Cc\\|Reply-To\\|From\\):")
|
||||
|
||||
|
@ -211,8 +216,7 @@ appear on disk."
|
|||
(mu4e~compose-set-friendly-buffer-name)
|
||||
(mu4e~draft-insert-mail-header-separator)
|
||||
;; hide some headers again
|
||||
(let ((message-hidden-headers mu4e~compose-hidden-headers))
|
||||
(message-hide-headers))
|
||||
(mu4e~compose-hide-headers)
|
||||
(set-buffer-modified-p nil)
|
||||
;; update the file on disk -- ie., without the separator
|
||||
(mu4e~proc-add (buffer-file-name) mu4e~draft-drafts-folder)) nil t))
|
||||
|
@ -255,7 +259,7 @@ appear on disk."
|
|||
(define-derived-mode mu4e-compose-mode message-mode "mu4e:compose"
|
||||
"Major mode for the mu4e message composition, derived from `message-mode'.
|
||||
\\{message-mode-map}."
|
||||
(let ((message-hidden-headers mu4e~compose-hidden-headers))
|
||||
(progn
|
||||
(use-local-map mu4e-compose-mode-map)
|
||||
(make-local-variable 'message-default-charset)
|
||||
;; if the default charset is not set, use UTF-8
|
||||
|
@ -279,7 +283,7 @@ appear on disk."
|
|||
|
||||
(define-key mu4e-compose-mode-map (kbd "C-S-u") 'mu4e-update-mail-and-index)
|
||||
(define-key mu4e-compose-mode-map (kbd "C-c C-u") 'mu4e-update-mail-and-index)
|
||||
|
||||
|
||||
;; setup the fcc-stuff, if needed
|
||||
(add-hook 'message-send-hook
|
||||
(defun mu4e~compose-save-before-sending ()
|
||||
|
@ -293,7 +297,7 @@ appear on disk."
|
|||
(setq mu4e-sent-func 'mu4e-sent-handler)
|
||||
(mu4e~proc-sent (buffer-file-name) mu4e~draft-drafts-folder)) nil))
|
||||
;; mark these two hooks as permanent-local, so they'll survive mode-changes
|
||||
;; (put 'mu4e~compose-save-before-sending 'permanent-local-hook t)
|
||||
;; (put 'mu4e~compose-save-before-sending 'permanent-local-hook t)
|
||||
(put 'mu4e~compose-mark-after-sending 'permanent-local-hook t))
|
||||
|
||||
(defconst mu4e~compose-buffer-max-name-length 30
|
||||
|
@ -312,12 +316,7 @@ appear on disk."
|
|||
(truncate-string-to-width str
|
||||
mu4e~compose-buffer-max-name-length
|
||||
nil nil t)))))
|
||||
|
||||
(defconst mu4e~compose-hidden-headers
|
||||
'("^References:" "^Face:" "^X-Face:" "^X-Draft-From:"
|
||||
"^User-Agent:" "^In-Reply-To:")
|
||||
"List of regexps with message headers that are to be hidden.")
|
||||
|
||||
|
||||
(defun mu4e~compose-handler (compose-type &optional original-msg includes)
|
||||
"Create a new draft message, or open an existing one.
|
||||
|
||||
|
@ -371,8 +370,7 @@ tempfile)."
|
|||
(set (make-local-variable 'mu4e-compose-parent-message) original-msg)
|
||||
(put 'mu4e-compose-parent-message 'permanent-local t)
|
||||
;; hide some headers
|
||||
(let ((message-hidden-headers mu4e~compose-hidden-headers))
|
||||
(message-hide-headers))
|
||||
(mu4e~compose-hide-headers)
|
||||
;; switch on the mode
|
||||
(mu4e-compose-mode))
|
||||
|
||||
|
|
|
@ -102,7 +102,7 @@ e-mail addresses. If LST is nil, returns nil."
|
|||
(let ((name (car addrcell))
|
||||
(email (cdr addrcell)))
|
||||
(if name
|
||||
(format "\"%s\" <%s>" name email)
|
||||
(format "%s <%s>" (mu4e~rfc822-quoteit name) email)
|
||||
(format "%s" email))))
|
||||
lst ", ")))
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
;; mu4e-mark.el -- part of mu4e, the mu mail user agent
|
||||
;;
|
||||
;; Copyright (C) 2011-2012 Dirk-Jan C. Binnema
|
||||
;; Copyright (C) 2011-2013 Dirk-Jan C. Binnema
|
||||
|
||||
;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
||||
;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
||||
|
@ -82,6 +82,24 @@ where
|
|||
"Clear the marks subsystem."
|
||||
(clrhash mu4e~mark-map))
|
||||
|
||||
|
||||
(defmacro mu4e~mark-in-context (&rest body)
|
||||
"Evaluate BODY in the context of the headers buffer in case this
|
||||
is either a headers or view buffer, and "
|
||||
`(cond
|
||||
((eq major-mode 'mu4e-headers-mode) ,@body)
|
||||
((eq major-mode 'mu4e-view-mode)
|
||||
(if (buffer-live-p mu4e~view-headers-buffer)
|
||||
(let* ((msg (mu4e-message-at-point))
|
||||
(docid (mu4e-message-field msg :docid)))
|
||||
(with-current-buffer mu4e~view-headers-buffer
|
||||
(if (mu4e~headers-goto-docid docid)
|
||||
,@body
|
||||
(mu4e-error "cannot find message in headers buffer."))))
|
||||
(mu4e-error "no headers buffer connected to view")))
|
||||
(t (progn (mu4e-message "%S" major-mode) ,@body))))
|
||||
|
||||
|
||||
(defun mu4e-mark-at-point (mark &optional target)
|
||||
"Mark (or unmark) message at point.
|
||||
MARK specifies the mark-type. For `move'-marks and `trash'-marks
|
||||
|
@ -229,19 +247,19 @@ as well."
|
|||
If there are such marks, replace them with a _real_ mark (ask the
|
||||
user which one)."
|
||||
(interactive)
|
||||
(let ((markpair))
|
||||
(maphash
|
||||
(lambda (docid val)
|
||||
(let ((mark (car val)) (target (cdr val)))
|
||||
(when (eql mark 'something)
|
||||
(unless markpair
|
||||
(setq markpair
|
||||
(mu4e~mark-get-markpair "Set deferred mark(s) to: " nil)))
|
||||
(save-excursion
|
||||
(when (mu4e~headers-goto-docid docid)
|
||||
(mu4e-mark-set (car markpair) (cdr markpair)))))))
|
||||
mu4e~mark-map)))
|
||||
|
||||
(mu4e~mark-in-context
|
||||
(let ((markpair))
|
||||
(maphash
|
||||
(lambda (docid val)
|
||||
(let ((mark (car val)) (target (cdr val)))
|
||||
(when (eql mark 'something)
|
||||
(unless markpair
|
||||
(setq markpair
|
||||
(mu4e~mark-get-markpair "Set deferred mark(s) to: " nil)))
|
||||
(save-excursion
|
||||
(when (mu4e~headers-goto-docid docid)
|
||||
(mu4e-mark-set (car markpair) (cdr markpair)))))))
|
||||
mu4e~mark-map))))
|
||||
|
||||
(defun mu4e~mark-check-target (target)
|
||||
"Check if the target exists if not, offer to create it."
|
||||
|
@ -264,46 +282,48 @@ work well.
|
|||
|
||||
If NO-CONFIRMATION is non-nil, don't ask user for confirmation."
|
||||
(interactive)
|
||||
(let ((marknum (hash-table-count mu4e~mark-map)))
|
||||
(if (zerop marknum)
|
||||
(message "Nothing is marked")
|
||||
(mu4e-mark-resolve-deferred-marks)
|
||||
(when (or no-confirmation
|
||||
(y-or-n-p
|
||||
(format "Are you sure you want to execute %d mark%s?"
|
||||
marknum (if (> marknum 1) "s" ""))))
|
||||
(maphash
|
||||
(lambda (docid val)
|
||||
(let ((mark (car val)) (target (cdr val)))
|
||||
;; note: whenever you do something with the message,
|
||||
;; it looses its N (new) flag
|
||||
(case mark
|
||||
(refile (mu4e~proc-move docid (mu4e~mark-check-target target) "-N"))
|
||||
(delete (mu4e~proc-remove docid))
|
||||
(flag (mu4e~proc-move docid nil "+F-u-N"))
|
||||
(move (mu4e~proc-move docid (mu4e~mark-check-target target) "-N"))
|
||||
(read (mu4e~proc-move docid nil "+S-u-N"))
|
||||
(trash (mu4e~proc-move docid (mu4e~mark-check-target target) "+T-N"))
|
||||
(unflag (mu4e~proc-move docid nil "-F-N"))
|
||||
(unread (mu4e~proc-move docid nil "-S+u-N"))
|
||||
(otherwise (mu4e-error "Unrecognized mark %S" mark)))))
|
||||
mu4e~mark-map))
|
||||
(mu4e-mark-unmark-all)
|
||||
(message nil))))
|
||||
(mu4e~mark-in-context
|
||||
(let ((marknum (hash-table-count mu4e~mark-map)))
|
||||
(if (zerop marknum)
|
||||
(message "Nothing is marked")
|
||||
(mu4e-mark-resolve-deferred-marks)
|
||||
(when (or no-confirmation
|
||||
(y-or-n-p
|
||||
(format "Are you sure you want to execute %d mark%s?"
|
||||
marknum (if (> marknum 1) "s" ""))))
|
||||
(maphash
|
||||
(lambda (docid val)
|
||||
(let ((mark (car val)) (target (cdr val)))
|
||||
;; note: whenever you do something with the message,
|
||||
;; it looses its N (new) flag
|
||||
(case mark
|
||||
(refile (mu4e~proc-move docid (mu4e~mark-check-target target) "-N"))
|
||||
(delete (mu4e~proc-remove docid))
|
||||
(flag (mu4e~proc-move docid nil "+F-u-N"))
|
||||
(move (mu4e~proc-move docid (mu4e~mark-check-target target) "-N"))
|
||||
(read (mu4e~proc-move docid nil "+S-u-N"))
|
||||
(trash (mu4e~proc-move docid (mu4e~mark-check-target target) "+T-N"))
|
||||
(unflag (mu4e~proc-move docid nil "-F-N"))
|
||||
(unread (mu4e~proc-move docid nil "-S+u-N"))
|
||||
(otherwise (mu4e-error "Unrecognized mark %S" mark)))))
|
||||
mu4e~mark-map))
|
||||
(mu4e-mark-unmark-all)
|
||||
(message nil)))))
|
||||
|
||||
(defun mu4e-mark-unmark-all ()
|
||||
"Unmark all marked messages."
|
||||
(interactive)
|
||||
(when (or (null mu4e~mark-map) (zerop (hash-table-count mu4e~mark-map)))
|
||||
(mu4e-warn "Nothing is marked"))
|
||||
(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))
|
||||
(mu4e~mark-in-context
|
||||
(when (or (null mu4e~mark-map) (zerop (hash-table-count mu4e~mark-map)))
|
||||
(mu4e-warn "Nothing is marked"))
|
||||
(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)))
|
||||
|
||||
(defun mu4e-mark-docid-marked-p (docid)
|
||||
"Is the given docid marked?"
|
||||
|
@ -313,25 +333,27 @@ If NO-CONFIRMATION is non-nil, don't ask user for confirmation."
|
|||
(defun mu4e-mark-marks-num ()
|
||||
"Return the number of marks in the current buffer."
|
||||
(if mu4e~mark-map (hash-table-count mu4e~mark-map) 0))
|
||||
|
||||
|
||||
(defun mu4e-mark-handle-when-leaving ()
|
||||
"If there are any marks in the current buffer, handle those
|
||||
according to the value of `mu4e-headers-leave-behavior'. This
|
||||
function is to be called before any further action (like searching,
|
||||
quiting the buffer) is taken; returning t means 'take the following
|
||||
action', return nil means 'don't do anything'"
|
||||
(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)
|
||||
(mu4e-mark-execute-all t)))))
|
||||
|
||||
action', return nil means 'don't do anything'."
|
||||
(mu4e~mark-in-context
|
||||
(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)
|
||||
(mu4e-mark-execute-all t))))))
|
||||
|
||||
|
||||
(provide 'mu4e-mark)
|
||||
;; End of mu4e-mark.el
|
||||
|
|
|
@ -553,13 +553,14 @@ process."
|
|||
|
||||
(defun mu4e~rfc822-phrase-type (ph)
|
||||
"Return either atom, quoted-string, a corner-case or nil. This
|
||||
checks for quotes around the phrase first
|
||||
checks for empty string first. Then quotes around the phrase
|
||||
(returning 'rfc822-quoted-string). Then whether there is a quote
|
||||
inside the phrase (returning 'rfc822-containing-quote).
|
||||
The reverse of the RFC atext definition is then tested.
|
||||
If it matches, nil is returned, if not, it is an 'rfc822-atom, which
|
||||
is returned."
|
||||
(cond
|
||||
((= (length ph) 0) 'rfc822-empty)
|
||||
((= (aref ph 0) ?\")
|
||||
(if (string-match "\"\\([^\"\\\n]\\|\\\\.\\|\\\\\n\\)*\"" ph)
|
||||
'rfc822-quoted-string
|
||||
|
|
|
@ -780,10 +780,10 @@ Also number them so they can be opened using `mu4e-view-go-to-url'."
|
|||
|
||||
|
||||
(defmacro mu4e~view-in-headers-context (&rest body)
|
||||
"Evaluate BODY in the current headers buffer, with moved to the
|
||||
current message."
|
||||
"Evaluate BODY in the context of the headers buffer connected to
|
||||
this view."
|
||||
`(progn
|
||||
(unless '(buffer-live-p mu4e~view-headers-buffer)
|
||||
(unless (buffer-live-p mu4e~view-headers-buffer)
|
||||
(mu4e-error "no headers-buffer connected"))
|
||||
(let* ((msg (mu4e-message-at-point))
|
||||
(docid (mu4e-message-field msg :docid)))
|
||||
|
|
|
@ -732,7 +732,7 @@ respectively. In the example, we use @code{:human-date}, which shows when the
|
|||
time when the message was sent today, and the date otherwise.
|
||||
@item The header field used for sorting is indicated by ``@t{V}'' or
|
||||
``@t{^}''@footnote{or you can use little graphical triangles; see variable
|
||||
@code{mu4e-use-fancy-chars}}, indicating the sort order (descending or
|
||||
@code{mu4e-use-fancy-chars}}, corresponding to the sort order (descending or
|
||||
ascending, respectively). You can influence this by a mouse click, or
|
||||
@key{O}. Not all fields allow sorting.
|
||||
@item Instead of showing the @t{From:} and @t{To:} fields separately, you
|
||||
|
@ -757,7 +757,7 @@ u=@emph{unread}. The tooltip for this field also contains this information.
|
|||
Jamie Zawinski's mail threading algorithm,
|
||||
@url{http://www.jwz.org/doc/threading.html}}.
|
||||
@item The headers view is @emph{automatically updated} if any changes are
|
||||
found during the indexing process, and if there is not current
|
||||
found during the indexing process, and if there is no current
|
||||
user-interaction. If you do not want such automatic updates, set
|
||||
@code{mu4e-headers-auto-update} to @code{nil}.
|
||||
@end itemize
|
||||
|
@ -1134,8 +1134,8 @@ is used for images.
|
|||
@section Displaying rich-text messages
|
||||
|
||||
@t{mu4e} normally prefers the plain-text version for messages that consist of
|
||||
both a plain-text and html (rich-text) versions of the body-text. You change
|
||||
this by setting @code{mu4e-view-prefer-html} to @t{t}.
|
||||
both a plain-text and html (rich-text) versions of the body-text. You can
|
||||
change this by setting @code{mu4e-view-prefer-html} to @t{t}.
|
||||
|
||||
If there is only an html-version, or if the plain-text version is too short in
|
||||
comparison with the html part@footnote{this is for the case where the
|
||||
|
@ -1225,7 +1225,8 @@ For more information, see the @command{mu-verify} manual page.
|
|||
@section Actions
|
||||
|
||||
You can perform custom functions (``actions'') on messages and their
|
||||
attachments. For a general discussion on how to define your own, see see @ref{Actions}.
|
||||
attachments. For a general discussion on how to define your own, see see
|
||||
@ref{Actions}.
|
||||
|
||||
@subsection Message actions
|
||||
|
||||
|
@ -2403,16 +2404,17 @@ shortcut character @key{o} is due to the first character of
|
|||
The @command{emacs}-package @t{sauron}@footnote{Sauron can be found at
|
||||
@url{https://github.com/djcb/sauron}, or in the Marmalade package-repository
|
||||
at @url{http://http://marmalade-repo.org/}} (by the same author) can be used
|
||||
to get notifications about new mails. If you put something like the below
|
||||
script in your @t{crontab} (or have some other way of having it execute every
|
||||
@emph{n} minutes) you receive notifications in the sauron-buffer when new
|
||||
messages arrive.
|
||||
to get notifications about new mails. If you run something like the below
|
||||
script from your @t{crontab} (or have some other way of having it execute
|
||||
every @emph{n} minutes), you receive notifications in the @t{sauron}-buffer
|
||||
when new messages arrive.
|
||||
|
||||
@verbatim
|
||||
#!/bin/sh
|
||||
# put the path to your Inbox folder here
|
||||
|
||||
# put the path to your Inbox folder here
|
||||
CHECKDIR="/home/$LOGNAME/Maildir/Inbox"
|
||||
|
||||
sauron-msg () {
|
||||
DBUS_COOKIE="/home/$LOGNAME/.sauron-dbus"
|
||||
if test "x$DBUS_SESSION_BUS_ADDRESS" = "x"; then
|
||||
|
@ -3011,7 +3013,7 @@ normal, synchronous fashion.
|
|||
@section Known issues
|
||||
|
||||
Although they are not really @emph{questions}, we end this chapter with a list
|
||||
of known issue and/or missing features in @t{mu4e}. Thus, users won't have to
|
||||
of known issues and/or missing features in @t{mu4e}. Thus, users won't have to
|
||||
search in vain for things that are not there (yet), and the author can use it
|
||||
as a todo-list.
|
||||
|
||||
|
@ -3020,11 +3022,24 @@ as a todo-list.
|
|||
utf-8}; so, if you problems with encodings, be sure to have
|
||||
@code{(set-language-environment "UTF-8")} in your @file{~/.emacs}.
|
||||
@item @emph{Thread handling is incomplete.} While threads are calculated and are
|
||||
visible in the headers buffer, you can not collapse/open them.
|
||||
visible in the headers buffer, you cannot collapse/open them.
|
||||
@item @emph{The key-bindings are @emph{somewhat} hard-coded.} That is, the main
|
||||
menu assumes the default key-bindings, as do the clicks-on-bookmarks.
|
||||
@item @emph{The @t{emacs} front-end of the @t{notmuch} e-mail indexer
|
||||
conflicts with @t{mu4e}}. @t{notmuch} running in parallel with
|
||||
@t{mu4e} leads to
|
||||
@verbatim
|
||||
error in process filter: mu4e-error-handler: Error 70: cannot read
|
||||
~/Maildir/...
|
||||
@end verbatim
|
||||
when sending a reply to a new e-mail. This seems to be caused by @t{notmuch}
|
||||
changing the name of the original message file while @t{mu4e} is working in on
|
||||
it. To prevent this, deactivate @t{notmuch} in your Emacs setup.
|
||||
@end itemize
|
||||
|
||||
For a more complete list, please refer to the issues-list in the
|
||||
github-repository.
|
||||
|
||||
|
||||
@node Tips and Tricks
|
||||
@appendix Tips and Tricks
|
||||
|
@ -3195,7 +3210,8 @@ If you have multiple accounts, you can accommodate them as well:
|
|||
((string-match "Account1" maildir)
|
||||
(setq folder (or (catch 'found
|
||||
(dolist (mailing-list my-mu4e-mailing-lists)
|
||||
(if (mu4e-message-contact-field-matches msg :to (car mailing-list))
|
||||
(if (mu4e-message-contact-field-matches
|
||||
msg :to (car mailing-list))
|
||||
(throw 'found (cdr mailing-list)))))
|
||||
"/Account1/General")))
|
||||
((string-match "Gmail" maildir)
|
||||
|
@ -3203,7 +3219,8 @@ If you have multiple accounts, you can accommodate them as well:
|
|||
((string-match "Account2" maildir)
|
||||
(setq folder (or (cdar (member* subject my-mu4e-subject-alist
|
||||
:test #'(lambda (x y)
|
||||
(string-match (car y) x))))
|
||||
(string-match
|
||||
(car y) x))))
|
||||
"/Account2/General"))))
|
||||
folder))
|
||||
@end lisp
|
||||
|
@ -3216,10 +3233,12 @@ message based on the mailing list to which it was sent. This requires
|
|||
another variable:
|
||||
|
||||
@lisp
|
||||
(defvar my-mu4e-mailing-lists '(("mu-discuss@@googlegroups.com" . "/Account1/mu4e")
|
||||
("pandoc-discuss@@googlegroups.com" . "/Account1/Pandoc")
|
||||
("auctex@@gnu.org" . "/Account1/AUCTeX"))
|
||||
"List of mailing list addresses and folders where their messages are saved.")
|
||||
(defvar my-mu4e-mailing-lists
|
||||
'(("mu-discuss@@googlegroups.com" . "/Account1/mu4e")
|
||||
("pandoc-discuss@@googlegroups.com" . "/Account1/Pandoc")
|
||||
("auctex@@gnu.org" . "/Account1/AUCTeX"))
|
||||
"List of mailing list addresses and folders where
|
||||
their messages are saved.")
|
||||
@end lisp
|
||||
|
||||
@node Saving outgoing messages
|
||||
|
|
Loading…
Reference in New Issue