Looking for the perfect mail client
Ian Main schrieb am 18:02 Freitag, 24.Oktober 2014: > Interesting, good feedback. Is it the usage that is difficult or setup? Installation was not a problem for me although the files ?notmuch.vim? and ?notmuch.txt? were not found by ?make install? for some reason. So I linked them into the notmuch-vim directory. After that ?make install? succeeded. But the main obstacle is that new users must find by trial and error that they must type ?s? to search, ?t? to tag files and so on. It would be very useful to list all the key bindings and configuration options. I still don't know how to compose a new message. > Well like any other text based client it uses a text dump of a links or such > (I > forget which one). I think ?elinks --dump? > We have great support for web browser viewing of the email > though if that isn't good enough. Yes, it's 2014. ;-) > Let me know if you have suggestions on where it's lacking. Okay, thank you so far.
[PATCH v3] VIM: Add URI handling
On Fri, Oct 24 2014, Ian Main wrote: This patch does not apply on top of notmuchmail master (commit 38240d106139da8). > This patch adds URI handling to the vim client. You can now press Although insignificant, I'll start commenting on all sent patches (that I look into) which talk like 'This patch adds' -- sure it is patch when email is sent, but in repository it is not so -- so the commit message should not mention it (but I do not require changing it). Anyway, I suspect this change will become applicable after some other change is committed first -- and IMO this could stay as non-stale patch provided that SomeOne(TM) informs what is the message that contains changes (or series those) that is required for this message to apply. BTW: does the content of this change differ much from v1 at id:1412281423-22441-1-git-send-email-imain at stemwinder.org or should I re-check (carefully!) that the changes are still OK. Tomi > 'enter' by default and the client will parse the current line and find > any 'Part's or URIs available for opening. If there are more than one > it opens the one under the cursor or else it opens the only one > available. It also supports mailto: URI's and will compose a new > message when activated. > > By default xdg-open is used for everything but mailto: which generally > does the right thing afaict. > > Note that this is now dependant on the attachment patch in order to > make the nice 'enter' behavior work for both. > > Ian > --- > > Fix commit message formatting. > > vim/notmuch.txt | 3 ++- > vim/notmuch.vim | 76 > +++-- > 2 files changed, 70 insertions(+), 9 deletions(-) > > diff --git a/vim/notmuch.txt b/vim/notmuch.txt > index 838a904..5d84fde 100644 > --- a/vim/notmuch.txt > +++ b/vim/notmuch.txt > @@ -74,7 +74,8 @@ I Mark as read (-unread) > tTag (prompted) > e Extract attachment on the current 'Attachment' line or all > attachments if the cursor is elsewhere. > -v View attachment on the current 'Attachment' line. > + View email part on the current 'Part' line, or open URI under cursor > +or on line. > sSearch > pSave patches > rReply > diff --git a/vim/notmuch.vim b/vim/notmuch.vim > index 1466e50..2f76f55 100644 > --- a/vim/notmuch.vim > +++ b/vim/notmuch.vim > @@ -12,7 +12,7 @@ let g:notmuch_folders_maps = { > \ '':'folders_show_search()', > \ 's': 'folders_search_prompt()', > \ '=': 'folders_refresh()', > - \ 'c': 'compose()', > + \ 'c': 'compose("")', > \ } > > let g:notmuch_search_maps = { > @@ -25,7 +25,7 @@ let g:notmuch_search_maps = { > \ 's': 'search_search_prompt()', > \ '=': 'search_refresh()', > \ '?': 'search_info()', > - \ 'c': 'compose()', > + \ 'c': 'compose("")', > \ } > > let g:notmuch_show_maps = { > @@ -35,13 +35,13 @@ let g:notmuch_show_maps = { > \ 't': 'show_tag("")', > \ 'o': 'show_open_msg()', > \ 'e': 'show_extract_msg()', > - \ '':'show_view_attachment()', > + \ '':'show_view_magic()', > \ 's': 'show_save_msg()', > \ 'p': 'show_save_patches()', > \ 'r': 'show_reply()', > \ '?': 'show_info()', > \ '': 'show_next_msg()', > - \ 'c': 'compose()', > + \ 'c': 'compose("")', > \ } > > let g:notmuch_compose_maps = { > @@ -63,6 +63,7 @@ let s:notmuch_view_attachment_default = 'xdg-open' > let s:notmuch_attachment_tmpdir_default = '~/.notmuch/tmp' > let s:notmuch_folders_count_threads_default = 0 > let s:notmuch_compose_start_insert_default = 1 > +let s:notmuch_open_uri_default = 'xdg-open' > > function! s:new_file_buffer(type, fname) > exec printf('edit %s', a:fname) > @@ -141,8 +142,8 @@ function! s:show_reply() > end > endfunction > > -function! s:compose() > - ruby open_compose > +function! s:compose(to_email) > + ruby open_compose(VIM::evaluate('a:to_email')) > let b:compose_done = 0 > call s:set_map(g:notmuch_compose_maps) > autocmd BufDelete call s:on_compose_delete() > @@ -155,6 +156,22 @@ function! s:show_info() > ruby vim_puts get_message.inspect > endfunction > > +function! s:show_view_magic() > + let line = getline(".") > + > +ruby << EOF > + line = VIM::evaluate('line') > + > + # Easiest to check for 'Part' types first.. > + match = line.match(/^Part (\d*):/) > + if match and match.length == 2 > + VIM::command('call s:show_view_attachment()') > + else > + VIM::command('call s:show_open_uri()') > + end > +EOF > +endfunction > + > function! s:show_view_attachment() > let line = getline(".") > ruby << EOF > @@ -226,6 +243,45 @@ ruby << EOF > EOF > endfunction > > +function! s:show_open_uri() > +
[PATCH v2 3/3] test: Update tests for 'authors_matched' and authors_non_matched'.
--- test/T160-json.sh| 9 + test/T170-sexp.sh| 4 ++-- test/T470-missing-headers.sh | 8 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/test/T160-json.sh b/test/T160-json.sh index c1cf649..0a8df18 100755 --- a/test/T160-json.sh +++ b/test/T160-json.sh @@ -25,6 +25,10 @@ test_expect_equal_json "$output" "[{\"thread\": \"XXX\", \"matched\": 1, \"total\": 1, \"authors\": \"Notmuch Test Suite\", + \"authors_matched\": [ + \"Notmuch Test Suite\" + ], + \"authors_non_matched\": [], \"subject\": \"json-search-subject\", \"query\": [\"id:$gen_msg_id\", null], \"tags\": [\"inbox\", @@ -59,6 +63,11 @@ test_expect_equal_json "$output" "[{\"thread\": \"XXX\", \"matched\": 1, \"total\": 1, \"authors\": \"Notmuch Test Suite\", + \"authors\": \"Notmuch Test Suite\", + \"authors_matched\": [ + \"Notmuch Test Suite\" + ], + \"authors_non_matched\": [], \"subject\": \"json-search-utf8-body-s?bj?ct\", \"query\": [\"id:$gen_msg_id\", null], \"tags\": [\"inbox\", diff --git a/test/T170-sexp.sh b/test/T170-sexp.sh index 667e319..f2a08bf 100755 --- a/test/T170-sexp.sh +++ b/test/T170-sexp.sh @@ -19,7 +19,7 @@ test_expect_equal "$output" ":id \"${gen_msg_id}\" :match t :excluded nil :f test_begin_subtest "Search message: sexp" add_message "[subject]=\"sexp-search-subject\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -\"" "[body]=\"sexp-search-message\"" output=$(notmuch search --format=sexp "sexp-search-message" | notmuch_search_sanitize) -test_expect_equal "$output" "((:thread \"0002\" :timestamp 946728000 :date_relative \"2000-01-01\" :matched 1 :total 1 :authors \"Notmuch Test Suite\" :subject \"sexp-search-subject\" :query (\"id:$gen_msg_id\" nil) :tags (\"inbox\" \"unread\")))" +test_expect_equal "$output" "((:thread \"0002\" :timestamp 946728000 :date_relative \"2000-01-01\" :matched 1 :total 1 :authors \"Notmuch Test Suite\" :authors_matched (\"Notmuch Test Suite\") :authors_non_matched () :subject \"sexp-search-subject\" :query (\"id:$gen_msg_id\" nil) :tags (\"inbox\" \"unread\")))" test_begin_subtest "Show message: sexp, utf-8" add_message "[subject]=\"sexp-show-utf8-body-s?bj?ct\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -\"" "[body]=\"js?n-show-m?ssage\"" @@ -44,7 +44,7 @@ test_expect_equal "$output" ":id \"$id\" :match t :excluded nil :filename \" test_begin_subtest "Search message: sexp, utf-8" add_message "[subject]=\"sexp-search-utf8-body-s?bj?ct\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -\"" "[body]=\"js?n-search-m?ssage\"" output=$(notmuch search --format=sexp "js?n-search-m?ssage" | notmuch_search_sanitize) -test_expect_equal "$output" "((:thread \"0005\" :timestamp 946728000 :date_relative \"2000-01-01\" :matched 1 :total 1 :authors \"Notmuch Test Suite\" :subject \"sexp-search-utf8-body-s?bj?ct\" :query (\"id:$gen_msg_id\" nil) :tags (\"inbox\" \"unread\")))" +test_expect_equal "$output" "((:thread \"0005\" :timestamp 946728000 :date_relative \"2000-01-01\" :matched 1 :total 1 :authors \"Notmuch Test Suite\" :authors_matched (\"Notmuch Test Suite\") :authors_non_matched () :subject \"sexp-search-utf8-body-s?bj?ct\" :query (\"id:$gen_msg_id\" nil) :tags (\"inbox\" \"unread\")))" test_done diff --git a/test/T470-missing-headers.sh b/test/T470-missing-headers.sh index cb38301..250a370 100755 --- a/test/T470-missing-headers.sh +++ b/test/T470-missing-headers.sh @@ -34,6 +34,10 @@ test_expect_equal_json "$output" ' [ { "authors": "", +"authors_matched": [ +"" +], +"authors_non_matched": [], "date_relative": "2001-01-05", "matched": 1, "subject": "", @@ -48,6 +52,10 @@ test_expect_equal_json "$output" ' }, { "authors": "Notmuch Test Suite", +"authors_matched": [ +"Notmuch Test Suite" +], +"authors_non_matched": [], "date_relative": "1970-01-01", "matched": 1, "subject": "", -- 2.1.1
[PATCH v2 2/3] emacs: Improved display of matching/non-matching authors.
Rather than splitting the :authors attribute, which is error prone, use the separate :authors_matched and :authors_non_matched attributes. This improves the display of authors should one of them include a pipe symbol (|) in their 'from' address. --- emacs/notmuch.el | 64 +++- 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/emacs/notmuch.el b/emacs/notmuch.el index b44a907..688b37c 100644 --- a/emacs/notmuch.el +++ b/emacs/notmuch.el @@ -672,22 +672,24 @@ foreground and blue background." ;; Reverse the list so earlier entries take precedence (reverse notmuch-search-line-faces))) -(defun notmuch-search-author-propertize (authors) +(defun notmuch-search-author-propertize (authors matching-length) "Split `authors' into matching and non-matching authors and propertize appropriately. If no boundary between authors and non-authors is found, assume that all of the authors match." - (if (string-match "\\(.*\\)|\\(.*\\)" authors) - (concat (propertize (concat (match-string 1 authors) ",") - 'face 'notmuch-search-matching-authors) - (propertize (match-string 2 authors) - 'face 'notmuch-search-non-matching-authors)) -(propertize authors 'face 'notmuch-search-matching-authors))) - -(defun notmuch-search-insert-authors (format-string authors) + (let ((match-part (substring authors 0 matching-length)) + (non-match-part (substring authors matching-length))) + + (concat (propertize match-part 'face 'notmuch-search-matching-authors) + (propertize non-match-part 'face 'notmuch-search-non-matching-authors + +(defun notmuch-search-insert-authors (format-string matching-authors non-matching-authors) ;; Save the match data to avoid interfering with ;; `notmuch-search-process-filter'. (save-match-data -(let* ((formatted-authors (format format-string authors)) +(let* ((authors (if (string= "" non-matching-authors) + matching-authors + (concat matching-authors ", " non-matching-authors))) + (formatted-authors (format format-string authors)) (formatted-sample (format format-string "")) (visible-string formatted-authors) (invisible-string "") @@ -703,9 +705,9 @@ non-authors is found, assume that all of the authors match." (setq visible-string (substring formatted-authors 0 visible-length) invisible-string (substring formatted-authors visible-length)) ;; If possible, truncate the visible string at a natural - ;; break (comma or pipe), as incremental search doesn't - ;; match across the visible/invisible border. - (when (string-match "\\(.*\\)\\([,|] \\)\\([^,|]*\\)" visible-string) + ;; break (comma), as incremental search doesn't match + ;; across the visible/invisible border. + (when (string-match "\\(.*\\)\\(, \\)\\([^,]*\\)" visible-string) ;; Second clause is destructive on `visible-string', so ;; order is important. (setq invisible-string (concat (match-string 3 visible-string) @@ -721,20 +723,23 @@ non-authors is found, assume that all of the authors match." ? ;; Use different faces to show matching and non-matching authors. - (if (string-match "\\(.*\\)|\\(.*\\)" visible-string) - ;; The visible string contains both matching and - ;; non-matching authors. - (setq visible-string (notmuch-search-author-propertize visible-string) - ;; The invisible string must contain only non-matching - ;; authors, as the visible-string contains both. - invisible-string (propertize invisible-string -'face 'notmuch-search-non-matching-authors)) - ;; The visible string contains only matching authors. - (setq visible-string (propertize visible-string -'face 'notmuch-search-matching-authors) - ;; The invisible string may contain both matching and - ;; non-matching authors. - invisible-string (notmuch-search-author-propertize invisible-string))) + (let ((visible-length (length visible-string)) + (matching-length (length matching-authors))) + + (if (> visible-length matching-length) + ;; The visible string contains both matching and + ;; non-matching authors. + (setq visible-string (notmuch-search-author-propertize visible-string matching-length) + ;; The invisible string must contain only non-matching + ;; authors, as the visible-string contains both. + invisible-string (propertize invisible-string + 'face
[PATCH v2 1/3] search: Separately report matching and non-matching authors.
In addition to the 'authors' attribute of each search result, include 'authors_matched' and 'authors_non_matched' attributes. Both attributes are always included and are formatted as a list of authors. If there are no matching authors, the 'authors_non_matched' attribute is set to the empty list. --- notmuch-search.c | 105 +++ 1 file changed, 105 insertions(+) diff --git a/notmuch-search.c b/notmuch-search.c index bc9be45..18c3b20 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -22,6 +22,8 @@ #include "sprinter.h" #include "string-util.h" +#include + typedef enum { OUTPUT_SUMMARY, OUTPUT_THREADS, @@ -69,6 +71,105 @@ get_thread_query (notmuch_thread_t *thread, return 0; } +/* Return a more pleasent rendering of the mail address + * `nasty_author'. */ +static const char * +_nice_author (void *ctx, const char *nasty_author) +{ +const char *nice_author = NULL; + +InternetAddressList *list = internet_address_list_parse_string (nasty_author); +if (list) { + InternetAddress *address = internet_address_list_get_address (list, 0); + if (address) { + nice_author = internet_address_get_name (address); + if (nice_author == NULL) { + InternetAddressMailbox *mailbox = INTERNET_ADDRESS_MAILBOX (address); + nice_author = internet_address_mailbox_get_addr (mailbox); + } + } + /* Duplicate the string before `g_object_unref' destroys +* it. */ + if (nice_author) + nice_author = talloc_strdup (ctx, nice_author); + + g_object_unref (G_OBJECT (list)); +} + +if (nice_author) + return nice_author; +else + return nasty_author; +} + +static int +_enumerate_authors (sprinter_t *format, +notmuch_thread_t *thread) +{ +notmuch_messages_t *messages; +GHashTable *matched_hash = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); +GHashTable *unmatched_hash = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); +GPtrArray *matched_array = g_ptr_array_new (); +GPtrArray *unmatched_array = g_ptr_array_new (); + +/* Iterate over the messages in the thread collecting matching and + * non-matching authors. */ +for (messages = notmuch_thread_get_messages (thread); +notmuch_messages_valid (messages); +notmuch_messages_move_to_next (messages)) +{ + notmuch_message_t *message = notmuch_messages_get (messages); + const char *author = _nice_author (thread, notmuch_message_get_header (message, "from")); + + if (author) { + GHashTable *hash; + GPtrArray *array; + + if (notmuch_message_get_flag (message, NOTMUCH_MESSAGE_FLAG_MATCH)) { + hash = matched_hash; + array = matched_array; + } else { + hash = unmatched_hash; + array = unmatched_array; + } + + if (!g_hash_table_lookup_extended (hash, author, NULL, NULL)) { + char *copy = talloc_strdup (thread, author); + g_hash_table_insert (hash, copy, NULL); + g_ptr_array_add (array, (char *) copy); + } + } +} + +/* Output the matched authors. */ +unsigned int i; +format->map_key (format, "authors_matched"); +format->begin_list (format); +for (i = 0; i < matched_array->len; i++) + format->string (format, (char *) g_ptr_array_index( matched_array, i)); +format->end (format); + +/* Output the non-matched authors, but not if they were seen + * already in the matched authors list. */ +format->map_key (format, "authors_non_matched"); +format->begin_list (format); +for (i = 0; i < unmatched_array->len; i++) { + char *author = (char *) g_ptr_array_index( unmatched_array, i); + + if (!g_hash_table_lookup_extended (matched_hash, author, NULL, NULL)) + format->string (format, author); +} +format->end (format); + +g_hash_table_unref (matched_hash); +g_hash_table_unref (unmatched_hash); + +g_ptr_array_free (matched_array, TRUE); +g_ptr_array_free (unmatched_array, TRUE); + +return 0; +} + static int do_search_threads (sprinter_t *format, notmuch_query_t *query, @@ -152,6 +253,10 @@ do_search_threads (sprinter_t *format, format->integer (format, total); format->map_key (format, "authors"); format->string (format, authors); + if (_enumerate_authors (format, thread) < 0) { + fprintf (stderr, "Out of memory\n"); + return 1; + } format->map_key (format, "subject"); format->string (format, subject); if (notmuch_format_version >= 2) { -- 2.1.1
[PATCH v2 0/3] Improve the display of matching/non-matching authors.
Improve the display of matching/non-matching authors. Distinguishing between matching and non-matching authors in the emacs interface is currently done by parsing the :authors attribute of a search result. If one of the authors uses the pipe symbol (|) in their 'From' address this parsing incorrectly determines the matching and non-matching authors. Address this by adding explicit matching and non-matching authors attributes to the structured output formats. v2: - Return the matching/non-matching authors as a list. - More improvements to the code that renders the authors are possible (to improve the chosen break between visible and invisible), but a planned re-write of the `notmuch-search-result-format' code would render that irrelevant. David Edmondson (3): search: Separately report matching and non-matching authors. emacs: Improved display of matching/non-matching authors. test: Update tests for 'authors_matched' and authors_non_matched'. emacs/notmuch.el | 64 ++ notmuch-search.c | 105 +++ test/T160-json.sh| 9 test/T170-sexp.sh| 4 +- test/T470-missing-headers.sh | 8 5 files changed, 159 insertions(+), 31 deletions(-) -- 2.1.1
mboxvievfs
Hi mboxviewfs is a FUSE filesystem program which shows mbox file as a separate files under -MM directories in a mountpoint. example usage transcript: $ cd ~ $ mkdir notmuchmailbox $ cd notmuchmailbox $ wget https://raw.githubusercontent.com/domo141/nottoomuch/master/mboxviewfs.c ... 2014-10-24 17:23:33 (524 KB/s) - ?mboxviewfs.c? saved [31966/31966] $ sh mboxviewfs.c + exec gcc -std=c99 -Wall -Wno-long-long -Wstrict-prototypes -pedantic -Wcast-align -Wpointer-arith -W -Wwrite-strings -Wcast-qual -Wshadow -O2 -o mboxviewfs mboxviewfs.c -D_FILE_OFFSET_BITS=64 -isystem /usr/include/fuse -pthread -lfuse $ wget -c http://notmuchmail.org/archives/notmuch.mbox $ mkdir notmuch $ ./mboxviewfs notmuch.mbox notmuch $ find notmuch -ls $ find notmuch | xargs stat -c '%x %n' $ wget -c http://notmuchmail.org/archives/notmuch.mbox $ fusermount -u notmuch $ ./mboxviewfs notmuch.mbox notmuch $ mkdir bin $ echo '#!/bin/sh' > bin/notmuch $ echo "HOME=$HOME/notmuchmailbox; export HOME" >> bin/notmuch $ echo "exec \"`which notmuch`\" \"\$@\"" >> bin/notmuch $ chmod 755 bin/notmuch $ mkdir mail $ mkdir mail/notmuch $ cd mail/notmuch $ : symbolic links to mail directories $ for d in ../../notmuch/*; do test -d "$d" || continue; ln -s "$d" .; done $ ls -l $ cd ../.. $ ./bin/notmuch setup ;: careful here, to run ./bin/notmuch '!!!' $ ./bin/notmuch new Found 19424 total files (that's not much mail). Warning: /home/too/notmuchmailbox/mail/notmuch/2009-11/02e4 is an mbox containing a single message, likely caused by misconfigured mail delivery. Support for single-message mboxes is deprecated and may be removed in the future. Processed 19424 total files in 1m 21s (239 files/sec.). Added 19414 new messages to the database. $ ./bin/notmuch count 19414 $ find notmuch -type f | wc 19424 19424 485600 $ : have to look that difference later... $ PATH=$PWD/bin:$PATH emacs -f notmuch 19 414 inbox 19 414 unread 40 unread-1d C-x C-c $ fusermount -u notmuch $ ./bin/notmuch count 19414 $ ./bin/notmuch new Error reading file /home/too/notmuchmailbox/mail/notmuch/2009-11: No such file or directory No new mail. Note: A fatal error was encountered: Something went wrong trying to read or write a file zsh: exit 1 ./bin/notmuch new $ wget -c http://notmuchmail.org/archives/notmuch.mbox $ ./mboxviewfs notmuch.mbox notmuch $ ./bin/notmuch new Warning: /home/too/notmuchmailbox/mail/notmuch/2014-10/4be0 is an mbox containing a single message, likely caused by misconfigured mail delivery. Support for single-message mboxes is deprecated and may be removed in the future. Processed 1 file in almost no time. Added 1 new message to the database. $ ./bin/notmuch count 19415 ---8<--- Next: to set nmbug to this system... Tomi
[O] how to put into a journal info about the email sent
On Fri, Oct 24 2014, Eric Abrahamsen wrote: > David Belohrad writes: > >> Dear All, >> >> i'm using org. And I'm using notmuch (that's why I address both mailing >> lists). Now, writing an email in everyday bussiness requires a >> non-significant time of your workhours. So I'd like to have this event >> in my org agenda. So any time I send some email with a given subject, >> I'd like to 'automatically' entry the information about it into >> e.g. sentmails.org in form of a diary entry, with appropriate tag. > > I do something like this in Gnorb, which I'd recommend you use except > it's mostly Gnus specific. > > I do it in two parts, but you could do it in one. Basically I add a > function to the `message-header-hook' (which ensures that all the > message headers have been generated properly). Does `message-generate-headers-first' not do what you want for this specific part? > Obviously the downside is that, without a "Gcc:" header, org can't > actually make a real link to the message. It doesn't know where it's > going to be. However if you know that all your sent messages can be > reached with a link that looks like "notmuch:id#Message-id", then you > can make that yourself in your org capture template with something like As you suggest, know the message-id should be good enough to generate a notmuch link, though you may have to wait for the notmuch index to be updated for the link to be valid.
how to put into a journal info about the email sent
Dear All, i'm using org. And I'm using notmuch (that's why I address both mailing lists). Now, writing an email in everyday bussiness requires a non-significant time of your workhours. So I'd like to have this event in my org agenda. So any time I send some email with a given subject, I'd like to 'automatically' entry the information about it into e.g. sentmails.org in form of a diary entry, with appropriate tag. In example: I'm sending a mail to Tom, with subject 'dealing vme register mapping'. At the moment I send this email (using smtpmail), I'd like an entry in the sentmails.org as follows: ** 2014-10 October *** 2014-10-02 Thursday dealing vme register mapping :mail: [[notmuch:id:7a97bb93e66a41878edd4c04fa764963 at cernfe03.cern.ch][dealing vme register mapping]] I thought that I can use following (add-hook 'message-send-hook ') to hook on sending and generate a capture entry. In fact this works pretty well _EXCEPT_ the link to the mail sent. The org-store-link cannot apparently store a link to an email, which so far was not sent (and not received?) because it claims that 'org-gnus-store-link: Can not create link: No Gcc header found' Hence this is pretty fatal for my diary entry. My question is: is there any way how to link not-yet-sent/received email as an org-link? Or is there any way to generate Gcc header before the email is sent and use this header during sending? Or is there any other way how to put into my agenda sent emails? many thanks david
Looking for the perfect mail client
Sepp Tannhuber wrote: > Ian Main schrieb am 18:02 Freitag, 24.Oktober 2014: > > > Interesting, good feedback. Is it the usage that is difficult or setup? > Installation was not a problem for me although the files ?notmuch.vim? and > ?notmuch.txt? were not found by ?make install? for some reason. So I linked > them into the notmuch-vim directory. After that ?make install? succeeded. > But the main obstacle is that new users must find by trial and error that > they must type ?s? to search, ?t? to tag files and so on. It would be very > useful to list all the key bindings and configuration options. > I still don't know how to compose a new message. https://github.com/imain/notmuch-vim/blob/master/doc/notmuch.txt Look at the 'MAPPINGS' section. I think a tutorial might be good though. > > Well like any other text based client it uses a text dump of a links or > > such (I > > forget which one). > I think ?elinks --dump? > > > We have great support for web browser viewing of the email > > though if that isn't good enough. > Yes, it's 2014. ;-) Time flies!! > > Let me know if you have suggestions on where it's lacking. > Okay, thank you so far. Thanks!
[PATCH v3 3/4] cli: Extend the search command for --output={sender, recipients}
Hi Mark, I mostly agree with your points mentioned in this and other your emails. I'll prepare v4 based on that. On Wed, Oct 22 2014, Mark Walters wrote: > On Sun, 12 Oct 2014, Michal Sojka wrote: >> The new outputs allow printing senders, recipients or both of matching >> messages. The --output option is converted from "keyword" argument to >> "flags" argument, which means that the user can use --output=sender and >> --output=recipients simultaneously, to print both. Other combinations >> produce an error. >> >> This code based on a patch from Jani Nikula. >> --- >> completion/notmuch-completion.bash | 2 +- >> completion/notmuch-completion.zsh | 3 +- >> doc/man1/notmuch-search.rst| 22 +++- >> notmuch-search.c | 110 >> ++--- >> test/T090-search-output.sh | 64 + >> 5 files changed, 189 insertions(+), 12 deletions(-) >> >> diff --git a/completion/notmuch-completion.bash >> b/completion/notmuch-completion.bash >> index 0571dc9..cfbd389 100644 >> --- a/completion/notmuch-completion.bash >> +++ b/completion/notmuch-completion.bash >> @@ -294,7 +294,7 @@ _notmuch_search() >> return >> ;; >> --output) >> -COMPREPLY=( $( compgen -W "summary threads messages files tags" -- >> "${cur}" ) ) >> +COMPREPLY=( $( compgen -W "summary threads messages files tags >> sender recipients" -- "${cur}" ) ) >> return >> ;; >> --sort) >> diff --git a/completion/notmuch-completion.zsh >> b/completion/notmuch-completion.zsh >> index 67a9aba..3e52a00 100644 >> --- a/completion/notmuch-completion.zsh >> +++ b/completion/notmuch-completion.zsh >> @@ -52,7 +52,8 @@ _notmuch_search() >>_arguments -s : \ >> '--max-threads=[display only the first x threads from the search >> results]:number of threads to show: ' \ >> '--first=[omit the first x threads from the search results]:number of >> threads to omit: ' \ >> -'--sort=[sort results]:sorting:((newest-first\:"reverse chronological >> order" oldest-first\:"chronological order"))' >> +'--sort=[sort results]:sorting:((newest-first\:"reverse chronological >> order" oldest-first\:"chronological order"))' \ >> +'--output=[select what to output]:output:((summary threads messages >> files tags sender recipients))' >> } >> >> _notmuch() >> diff --git a/doc/man1/notmuch-search.rst b/doc/man1/notmuch-search.rst >> index 90160f2..c9d38b1 100644 >> --- a/doc/man1/notmuch-search.rst >> +++ b/doc/man1/notmuch-search.rst >> @@ -35,7 +35,7 @@ Supported options for **search** include >> intended for programs that invoke **notmuch(1)** internally. If >> omitted, the latest supported version will be used. >> >> -``--output=(summary|threads|messages|files|tags)`` >> +``--output=(summary|threads|messages|files|tags|sender|recipients)`` >> >> **summary** >> Output a summary of each thread with any message matching >> @@ -78,6 +78,26 @@ Supported options for **search** include >> by null characters (--format=text0), as a JSON array >> (--format=json), or as an S-Expression list (--format=sexp). >> >> +**sender** >> +Output all addresses from the *From* header that appear on >> +any message matching the search terms, either one per line >> +(--format=text), separated by null characters >> +(--format=text0), as a JSON array (--format=json), or as >> +an S-Expression list (--format=sexp). >> + >> +Note: Searching for **sender** should be much faster than >> +searching for **recipients**, because sender addresses are >> +cached directly in the database whereas other addresses >> +need to be fetched from message files. >> + >> +**recipients** >> +Like **sender** but for addresses from *To*, *Cc* and >> +*Bcc* headers. >> + >> +This option can be given multiple times to combine different >> +outputs. Curently, this is only supported for **sender** and >> +**recipients** outputs. >> + >> ``--sort=``\ (**newest-first**\ \|\ **oldest-first**) >> This option can be used to present results in either >> chronological order (**oldest-first**) or reverse chronological >> diff --git a/notmuch-search.c b/notmuch-search.c >> index 5ac2a26..74588f8 100644 >> --- a/notmuch-search.c >> +++ b/notmuch-search.c >> @@ -23,11 +23,14 @@ >> #include "string-util.h" >> >> typedef enum { >> -OUTPUT_SUMMARY, >> -OUTPUT_THREADS, >> -OUTPUT_MESSAGES, >> -OUTPUT_FILES, >> -OUTPUT_TAGS >> +OUTPUT_SUMMARY = 1 << 0, >> +OUTPUT_THREADS = 1 << 1, >> +OUTPUT_MESSAGES = 1 << 2, >> +OUTPUT_FILES= 1 << 3, >> +OUTPUT_TAGS = 1 << 4, >> +OUTPUT_SENDER = 1 << 5, >> +OUTPUT_RECIPIENTS = 1 << 6, >> +OUTPUT_ADDRESSES= OUTPUT_SENDER |
[PATCH v2] VIM: Add URI handling
On Fri, Oct 24 2014, Ian Main wrote: > Add URI handling to the vim client. You can now press 'enter' by default and > the client will parse the current line and find any 'Part's or URIs available > for opening. If there are more than one it opens the one under the cursor or > else it opens the only one available. It also supports mailto: URI's and will > compose a new message when activated. The lines are way too long in this commit message. 72-chars except in cases where there is no word breaks in the text. id:1414101891-10714-1-git-send-email-imain at stemwinder.org has also one 75-character line (and talks about 'This patch ...' which could be changed too (but is tolerated if not)). Tomi > > By default xdg-open is used for everything but mailto: which generally > does the right thing afaict. > > Note that this is now dependant on the attachment patch in order to make > the nice 'enter' behavior work for both. > > Ian > --- > vim/notmuch.txt | 3 ++- > vim/notmuch.vim | 76 > +++-- > 2 files changed, 70 insertions(+), 9 deletions(-) > > diff --git a/vim/notmuch.txt b/vim/notmuch.txt > index 838a904..5d84fde 100644 > --- a/vim/notmuch.txt > +++ b/vim/notmuch.txt > @@ -74,7 +74,8 @@ I Mark as read (-unread) > tTag (prompted) > e Extract attachment on the current 'Attachment' line or all > attachments if the cursor is elsewhere. > -v View attachment on the current 'Attachment' line. > + View email part on the current 'Part' line, or open URI under cursor > +or on line. > sSearch > pSave patches > rReply > diff --git a/vim/notmuch.vim b/vim/notmuch.vim > index 1466e50..2f76f55 100644 > --- a/vim/notmuch.vim > +++ b/vim/notmuch.vim > @@ -12,7 +12,7 @@ let g:notmuch_folders_maps = { > \ '':'folders_show_search()', > \ 's': 'folders_search_prompt()', > \ '=': 'folders_refresh()', > - \ 'c': 'compose()', > + \ 'c': 'compose("")', > \ } > > let g:notmuch_search_maps = { > @@ -25,7 +25,7 @@ let g:notmuch_search_maps = { > \ 's': 'search_search_prompt()', > \ '=': 'search_refresh()', > \ '?': 'search_info()', > - \ 'c': 'compose()', > + \ 'c': 'compose("")', > \ } > > let g:notmuch_show_maps = { > @@ -35,13 +35,13 @@ let g:notmuch_show_maps = { > \ 't': 'show_tag("")', > \ 'o': 'show_open_msg()', > \ 'e': 'show_extract_msg()', > - \ '':'show_view_attachment()', > + \ '':'show_view_magic()', > \ 's': 'show_save_msg()', > \ 'p': 'show_save_patches()', > \ 'r': 'show_reply()', > \ '?': 'show_info()', > \ '': 'show_next_msg()', > - \ 'c': 'compose()', > + \ 'c': 'compose("")', > \ } > > let g:notmuch_compose_maps = { > @@ -63,6 +63,7 @@ let s:notmuch_view_attachment_default = 'xdg-open' > let s:notmuch_attachment_tmpdir_default = '~/.notmuch/tmp' > let s:notmuch_folders_count_threads_default = 0 > let s:notmuch_compose_start_insert_default = 1 > +let s:notmuch_open_uri_default = 'xdg-open' > > function! s:new_file_buffer(type, fname) > exec printf('edit %s', a:fname) > @@ -141,8 +142,8 @@ function! s:show_reply() > end > endfunction > > -function! s:compose() > - ruby open_compose > +function! s:compose(to_email) > + ruby open_compose(VIM::evaluate('a:to_email')) > let b:compose_done = 0 > call s:set_map(g:notmuch_compose_maps) > autocmd BufDelete call s:on_compose_delete() > @@ -155,6 +156,22 @@ function! s:show_info() > ruby vim_puts get_message.inspect > endfunction > > +function! s:show_view_magic() > + let line = getline(".") > + > +ruby << EOF > + line = VIM::evaluate('line') > + > + # Easiest to check for 'Part' types first.. > + match = line.match(/^Part (\d*):/) > + if match and match.length == 2 > + VIM::command('call s:show_view_attachment()') > + else > + VIM::command('call s:show_open_uri()') > + end > +EOF > +endfunction > + > function! s:show_view_attachment() > let line = getline(".") > ruby << EOF > @@ -226,6 +243,45 @@ ruby << EOF > EOF > endfunction > > +function! s:show_open_uri() > + let line = getline(".") > + let pos = getpos(".") > + let col = pos[2] > +ruby << EOF > + m = get_message > + line = VIM::evaluate('line') > + col = VIM::evaluate('col') - 1 > + uris = URI.extract(line) > + wanted_uri = nil > + if uris.length == 1 > + wanted_uri = uris[0] > + else > + uris.each do |uri| > + # Check to see the URI is at the present cursor location > + idx = line.index(uri) > + if col >= idx and col <= idx + uri.length > +
[PATCH v3 3/4] cli: Extend the search command for --output={sender, recipients}
On Thu, Oct 23 2014, Mark Walters wrote: > On Sun, 12 Oct 2014, Michal Sojka wrote: >> The new outputs allow printing senders, recipients or both of matching >> messages. The --output option is converted from "keyword" argument to >> "flags" argument, which means that the user can use --output=sender and >> --output=recipients simultaneously, to print both. Other combinations >> produce an error. >> >> ... >> >> +static void >> +print_address_list (const search_options_t *o, InternetAddressList *list) >> +{ >> +InternetAddress *address; >> +int i; >> + >> +for (i = 0; i < internet_address_list_length (list); i++) { >> +address = internet_address_list_get_address (list, i); >> +if (INTERNET_ADDRESS_IS_GROUP (address)) { >> +InternetAddressGroup *group; >> +InternetAddressList *group_list; >> + >> +group = INTERNET_ADDRESS_GROUP (address); >> +group_list = internet_address_group_get_members (group); >> +if (group_list == NULL) >> +continue; >> + >> +print_address_list (o, group_list); >> +} else { >> +InternetAddressMailbox *mailbox; >> +const char *name; >> +const char *addr; >> +char *full_address; >> + >> +mailbox = INTERNET_ADDRESS_MAILBOX (address); >> + >> +name = internet_address_get_name (address); >> +addr = internet_address_mailbox_get_addr (mailbox); >> + >> +if (name && *name) >> +full_address = talloc_asprintf (o->format, "%s <%s>", name, >> addr); >> +else >> +full_address = talloc_strdup (o->format, addr); >> + >> +if (!full_address) { >> +fprintf (stderr, "Error: out of memory\n"); >> +break; >> +} >> +o->format->string (o->format, full_address); >> +o->format->separator (o->format); >> + >> +talloc_free (full_address); > > Thinking about this some more how about printing the name and address as > a structured pair/map (at least for all cases except text/text0 output): > something like (in JSON) > [name: "John Doe" address: "john.doe at example.com"] > > It seems wrong to me to go to the effort of separating them in the C and > then combining them in the output. > > This could also help with the questions about uniqueness. If the client > can get the data ready parsed into name/address then it can deal with > much of the uniqueness itself. In that case client can also filter based on some substring, reducing the memory requirements... > > My preference would be for the default to print one line for each > distinct full_address, and then any filter-by options to refine from > there. Hmm, now I cannot decide whether this or just print out all addresses of messages, or do this distinct full_address output -- it looks like all other --output options prints unique lines, but there is potential of quite a lot of memory usage there... ... probably the memory usage is not problem there, OOM-killer eventually does it's job if necessary (!) (but machine may be slow (and trashing) for a while (just thinking out loud)) (!) but could we have general filter option for search to drop data before it is even considered for caching! -- maybe later ? > One other advantage of structuring the output is that it is extensible: > for example, at some later stage, we could include a "count" in the map > allowing the client can pick the most popular variant. , and in this case notmuch cannot print any output until the full address list is gathered... :D > > Best wishes > > Mark Tomi > > > > >> +} >> +} >> +} >> + >> +static void >> +print_address_string (const search_options_t *o, const char *recipients) >> +{ >> +InternetAddressList *list; >> + >> +if (recipients == NULL) >> +return; >> + >> +list = internet_address_list_parse_string (recipients); >> +if (list == NULL) >> +return; >> + >> +print_address_list (o, list); >> +} >> + >> static int >> do_search_messages (search_options_t *o) >> { >> @@ -266,11 +330,29 @@ do_search_messages (search_options_t *o) >> >> notmuch_filenames_destroy( filenames ); >> >> -} else { /* output == OUTPUT_MESSAGES */ >> +} else if (o->output == OUTPUT_MESSAGES) { >> format->set_prefix (format, "id"); >> format->string (format, >> notmuch_message_get_message_id (message)); >> format->separator (format); >> +} else { >> +if (o->output & OUTPUT_SENDER) { >> +const char *addrs; >> + >> +addrs = notmuch_message_get_header (message, "from"); >> +print_address_string (o, addrs); >> +} >> + >> +if (o->output & OUTPUT_RECIPIENTS) { >> +const char *hdrs[] = { "to", "cc", "bcc" }; >> +const char *addrs; >> +size_t j; >> + >> +for (j = 0; j < ARRAY_SIZE (hdrs); j++) { >> +addrs =
[PATCH] doc: add README.md especially for github
On Fri, Oct 24 2014, David Bremner wrote: > Apparently README.md overrides README, so this will show up instead of > our generic README on github. > > If the user is already on github, then clicking a link for more > information is not a hardship. LGTM. although I'd prefer README.rst Tomi > --- > README.md | 15 +++ > 1 file changed, 15 insertions(+) > create mode 100644 README.md > > diff --git a/README.md b/README.md > new file mode 100644 > index 000..ce579d8 > --- /dev/null > +++ b/README.md > @@ -0,0 +1,15 @@ > +If you're reading this on github.com, this is a read-only mirror of > +the notmuch project. > + > +For more information about the project, see > + > + http://notmuchmail.org > + > +Please don't send us pull requests on github. If you have a feature > +branch that you want us to look at, use `git send-email` to send to > +notmuch at notmuchmail.org. > + > +For more information about contributing to the project, see > + > + > + http://notmuchmail.org/contributing/ > -- > 2.1.1 > > ___ > notmuch mailing list > notmuch at notmuchmail.org > http://notmuchmail.org/mailman/listinfo/notmuch
[PATCH] doc: add README.md especially for github
Tomi Ollila writes: > On Fri, Oct 24 2014, David Bremner wrote: > >> Apparently README.md overrides README, so this will show up instead of >> our generic README on github. >> >> If the user is already on github, then clicking a link for more >> information is not a hardship. > > LGTM. although I'd prefer README.rst > OK, rst version pushed and shows up on github. d
[PATCH v3 3/4] cli: Extend the search command for --output={sender, recipients}
On Thu, Oct 23 2014, Mark Walters wrote: > Thinking about this some more how about printing the name and address as > a structured pair/map (at least for all cases except text/text0 output): > something like (in JSON) > [name: "John Doe" address: "john.doe at example.com"] > > It seems wrong to me to go to the effort of separating them in the C and > then combining them in the output. Agreed, this would be convenient.
[PATCH v1 1/3] search: Seperately report matching and non-matching authors.
On Fri, Oct 24 2014, Mark Walters wrote: > What about having both authors_matched and authors_not_matched as lists > of authors (ie one string for each author)? That's a sensible idea. I will look into it. > Then emacs, for example, wouldn't try and parse the string back into > authors before splitting. It doesn't really do that other than to decide where to truncate the visible string. That whole chunk of code is horrible. >> first_non_matched_author = 0; > > I think I would prefer to make this look like the matched case and drop > the first_non_matched_author stuff. Agreed, will do.
[PATCH] doc: add README.md especially for github
Apparently README.md overrides README, so this will show up instead of our generic README on github. If the user is already on github, then clicking a link for more information is not a hardship. --- README.md | 15 +++ 1 file changed, 15 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 000..ce579d8 --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +If you're reading this on github.com, this is a read-only mirror of +the notmuch project. + +For more information about the project, see + + http://notmuchmail.org + +Please don't send us pull requests on github. If you have a feature +branch that you want us to look at, use `git send-email` to send to +notmuch at notmuchmail.org. + +For more information about contributing to the project, see + + + http://notmuchmail.org/contributing/ -- 2.1.1
[PATCH v1 1/3] search: Seperately report matching and non-matching authors.
Hi I definitely like the idea: some comments below. On Fri, 24 Oct 2014, David Edmondson wrote: > In addition to the :authors attribute of each search result, include > :authors_matched and :authors_non_matched attributes. Both attributes > are always included. If there are no non-matching authors, the > :authors_non_matched attribute is set to the empty string. What about having both authors_matched and authors_not_matched as lists of authors (ie one string for each author)? Then emacs, for example, wouldn't try and parse the string back into authors before splitting. And authors_non_matched could be an empty list when appropriate which seems more natural than the empty string. All the above is based on what a client might want in the output rather than what is easy or sensible to implement in the C code. > --- > lib/notmuch.h| 34 > lib/thread.cc| 60 > +++- > notmuch-search.c | 6 ++ > 3 files changed, 82 insertions(+), 18 deletions(-) > > diff --git a/lib/notmuch.h b/lib/notmuch.h > index dae0416..30ce6c3 100644 > --- a/lib/notmuch.h > +++ b/lib/notmuch.h > @@ -993,6 +993,40 @@ const char * > notmuch_thread_get_authors (notmuch_thread_t *thread); > > /** > + * Get the matched authors of 'thread' as a UTF-8 string. > + * > + * The returned string is a comma-separated list of the names of the > + * authors of mail messages in the query results that belong to this > + * thread. > + * > + * Authors are ordered by date. > + * > + * The returned string belongs to 'thread' and as such, should not be > + * modified by the caller and will only be valid for as long as the > + * thread is valid, (which is until notmuch_thread_destroy or until > + * the query from which it derived is destroyed). > + */ > +const char * > +notmuch_thread_get_authors_matched (notmuch_thread_t *thread); > + > +/** > + * Get the non-matched authors of 'thread' as a UTF-8 string. > + * > + * The returned string is a comma-separated list of the names of the > + * authors of mail messages in the query results that belong to this > + * thread. > + * > + * Authors are ordered by date. > + * > + * The returned string belongs to 'thread' and as such, should not be > + * modified by the caller and will only be valid for as long as the > + * thread is valid, (which is until notmuch_thread_destroy or until > + * the query from which it derived is destroyed). > + */ > +const char * > +notmuch_thread_get_authors_non_matched (notmuch_thread_t *thread); > + > +/** > * Get the subject of 'thread' as a UTF-8 string. > * > * The subject is taken from the first message (according to the query > diff --git a/lib/thread.cc b/lib/thread.cc > index 8922403..b344875 100644 > --- a/lib/thread.cc > +++ b/lib/thread.cc > @@ -33,6 +33,8 @@ struct visible _notmuch_thread { > GHashTable *matched_authors_hash; > GPtrArray *matched_authors_array; > char *authors; > +char *authors_matched; > +char *authors_non_matched; > GHashTable *tags; > > /* All messages, oldest first. */ > @@ -112,10 +114,11 @@ _thread_add_matched_author (notmuch_thread_t *thread, > g_ptr_array_add (thread->matched_authors_array, author_copy); > } > > -/* Construct an authors string from matched_authors_array and > - * authors_array. The string contains matched authors first, then > - * non-matched authors (with the two groups separated by '|'). Within > - * each group, authors are listed in date order. */ > +/* Construct the authors_matched, authors_non_matched and authors > + * strings from matched_authors_array and authors_array. The authors > + * string contains matched authors first, then non-matched authors > + * (with the two groups separated by '|'). Within each group, authors > + * are listed in date order. */ > static void > _resolve_thread_authors_string (notmuch_thread_t *thread) > { > @@ -123,36 +126,43 @@ _resolve_thread_authors_string (notmuch_thread_t > *thread) > char *author; > int first_non_matched_author = 1; > > -/* First, list all matched authors in date order. */ > +/* List all matched authors in date order. */ > for (i = 0; i < thread->matched_authors_array->len; i++) { > author = (char *) g_ptr_array_index (thread->matched_authors_array, i); > - if (thread->authors) > - thread->authors = talloc_asprintf (thread, "%s, %s", > -thread->authors, > -author); > - else > - thread->authors = author; > + if (thread->authors_matched) { > + thread->authors_matched = talloc_asprintf (thread, "%s, %s", > +thread->authors_matched, > +author); > + } else { > + thread->authors_matched = author; > + } > } > > -/* Next, append any non-matched
[PATCH v3] VIM: Add URI handling
Tomi Ollila wrote: > On Fri, Oct 24 2014, Ian Main wrote: > > This patch does not apply on top of notmuchmail master > (commit 38240d106139da8). > > > This patch adds URI handling to the vim client. You can now press > > Although insignificant, I'll start commenting on all sent patches > (that I look into) which talk like 'This patch adds' -- sure it is patch > when email is sent, but in repository it is not so -- so the commit message > should not mention it (but I do not require changing it). > > Anyway, I suspect this change will become applicable after some other > change is committed first -- and IMO this could stay as non-stale patch > provided that SomeOne(TM) informs what is the message that contains > changes (or series those) that is required for this message to apply. > > BTW: does the content of this change differ much from v1 at > id:1412281423-22441-1-git-send-email-imain at stemwinder.org > or should I re-check (carefully!) that the changes are still OK. > > > Tomi > This one is almost identical minus the hotkey usage. The attachment support patch has changed a fair bit in that it now looks at all parts of multipart messages, giving you the opportunity to view the original text/html message in a web browser. I sent them out as a series in a new thread so that it would be easier to follow. I have them all applied locally and there were some conflicts to resolve. They are simple, usually around the defaults settings for the configuration but it's still a pain. I'm still tempted to send out all of the outstanding patches as a big series. All the patches posted though should apply to master so I may just have to rebase now and again. Ian
[O] how to put into a journal info about the email sent
David Edmondson writes: > On Fri, Oct 24 2014, Eric Abrahamsen wrote: >> David Belohrad writes: >> >>> Dear All, >>> >>> i'm using org. And I'm using notmuch (that's why I address both mailing >>> lists). Now, writing an email in everyday bussiness requires a >>> non-significant time of your workhours. So I'd like to have this event >>> in my org agenda. So any time I send some email with a given subject, >>> I'd like to 'automatically' entry the information about it into >>> e.g. sentmails.org in form of a diary entry, with appropriate tag. >> >> I do something like this in Gnorb, which I'd recommend you use except >> it's mostly Gnus specific. >> >> I do it in two parts, but you could do it in one. Basically I add a >> function to the `message-header-hook' (which ensures that all the >> message headers have been generated properly). > > Does `message-generate-headers-first' not do what you want for this > specific part? Yeah, I think I looked at that previously. But this thing is going in a hook anyway, might as well use the hook that *doesn't* require me to call that function explicitly. >> Obviously the downside is that, without a "Gcc:" header, org can't >> actually make a real link to the message. It doesn't know where it's >> going to be. However if you know that all your sent messages can be >> reached with a link that looks like "notmuch:id#Message-id", then you >> can make that yourself in your org capture template with something like > > As you suggest, know the message-id should be good enough to generate a > notmuch link, though you may have to wait for the notmuch index to be > updated for the link to be valid. Yup, I've got the same issue with nnimap -- you have to wait for the next sync to get access to the message. So far it hasn't been a problem, though.
Looking for the perfect mail client
Ian Main schrieb am 6:28 Freitag, 24.Oktober 2014: > I wonder if you'd be willing to give the vim client a go. We've done a lot > of work to it lately and I think it would handle all you listed here. For me, it's hard to get familiar with it because I have not found a detailed documentation. The README file is not good enough for a stupid person. I also doubt that it can render html properly. Nevertheless, I would give it a try if I could find useful documentation.
[PATCH v1 3/3] test: Update tests for :authors_matched and :authors_non_matched.
--- test/T160-json.sh| 4 test/T170-sexp.sh| 4 ++-- test/T470-missing-headers.sh | 4 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/test/T160-json.sh b/test/T160-json.sh index c1cf649..d3cf2e7 100755 --- a/test/T160-json.sh +++ b/test/T160-json.sh @@ -25,6 +25,8 @@ test_expect_equal_json "$output" "[{\"thread\": \"XXX\", \"matched\": 1, \"total\": 1, \"authors\": \"Notmuch Test Suite\", + \"authors_matched\": \"Notmuch Test Suite\", + \"authors_non_matched\": \"\", \"subject\": \"json-search-subject\", \"query\": [\"id:$gen_msg_id\", null], \"tags\": [\"inbox\", @@ -59,6 +61,8 @@ test_expect_equal_json "$output" "[{\"thread\": \"XXX\", \"matched\": 1, \"total\": 1, \"authors\": \"Notmuch Test Suite\", + \"authors_matched\": \"Notmuch Test Suite\", + \"authors_non_matched\": \"\", \"subject\": \"json-search-utf8-body-s?bj?ct\", \"query\": [\"id:$gen_msg_id\", null], \"tags\": [\"inbox\", diff --git a/test/T170-sexp.sh b/test/T170-sexp.sh index 667e319..8a57b74 100755 --- a/test/T170-sexp.sh +++ b/test/T170-sexp.sh @@ -19,7 +19,7 @@ test_expect_equal "$output" ":id \"${gen_msg_id}\" :match t :excluded nil :f test_begin_subtest "Search message: sexp" add_message "[subject]=\"sexp-search-subject\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -\"" "[body]=\"sexp-search-message\"" output=$(notmuch search --format=sexp "sexp-search-message" | notmuch_search_sanitize) -test_expect_equal "$output" "((:thread \"0002\" :timestamp 946728000 :date_relative \"2000-01-01\" :matched 1 :total 1 :authors \"Notmuch Test Suite\" :subject \"sexp-search-subject\" :query (\"id:$gen_msg_id\" nil) :tags (\"inbox\" \"unread\")))" +test_expect_equal "$output" "((:thread \"0002\" :timestamp 946728000 :date_relative \"2000-01-01\" :matched 1 :total 1 :authors \"Notmuch Test Suite\" :authors_matched \"Notmuch Test Suite\" :authors_non_matched \"\" :subject \"sexp-search-subject\" :query (\"id:$gen_msg_id\" nil) :tags (\"inbox\" \"unread\")))" test_begin_subtest "Show message: sexp, utf-8" add_message "[subject]=\"sexp-show-utf8-body-s?bj?ct\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -\"" "[body]=\"js?n-show-m?ssage\"" @@ -44,7 +44,7 @@ test_expect_equal "$output" ":id \"$id\" :match t :excluded nil :filename \" test_begin_subtest "Search message: sexp, utf-8" add_message "[subject]=\"sexp-search-utf8-body-s?bj?ct\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -\"" "[body]=\"js?n-search-m?ssage\"" output=$(notmuch search --format=sexp "js?n-search-m?ssage" | notmuch_search_sanitize) -test_expect_equal "$output" "((:thread \"0005\" :timestamp 946728000 :date_relative \"2000-01-01\" :matched 1 :total 1 :authors \"Notmuch Test Suite\" :subject \"sexp-search-utf8-body-s?bj?ct\" :query (\"id:$gen_msg_id\" nil) :tags (\"inbox\" \"unread\")))" +test_expect_equal "$output" "((:thread \"0005\" :timestamp 946728000 :date_relative \"2000-01-01\" :matched 1 :total 1 :authors \"Notmuch Test Suite\" :authors_matched \"Notmuch Test Suite\" :authors_non_matched \"\" :subject \"sexp-search-utf8-body-s?bj?ct\" :query (\"id:$gen_msg_id\" nil) :tags (\"inbox\" \"unread\")))" test_done diff --git a/test/T470-missing-headers.sh b/test/T470-missing-headers.sh index cb38301..5d66e7e 100755 --- a/test/T470-missing-headers.sh +++ b/test/T470-missing-headers.sh @@ -34,6 +34,8 @@ test_expect_equal_json "$output" ' [ { "authors": "", +"authors_matched": "", +"authors_non_matched": "", "date_relative": "2001-01-05", "matched": 1, "subject": "", @@ -48,6 +50,8 @@ test_expect_equal_json "$output" ' }, { "authors": "Notmuch Test Suite", +"authors_matched": "Notmuch Test Suite", +"authors_non_matched": "", "date_relative": "1970-01-01", "matched": 1, "subject": "", -- 2.1.1
[PATCH v1 2/3] emacs: Improved display of matching/non-matching authors.
Rather than splitting the :authors attribute, which is error prone, use the separate :authors_matched and :authors_non_matched attributes. This improves the display of authors should one of them include a pipe symbol (|) in their 'from' address. --- emacs/notmuch.el | 64 +++- 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/emacs/notmuch.el b/emacs/notmuch.el index b44a907..29b6cdc 100644 --- a/emacs/notmuch.el +++ b/emacs/notmuch.el @@ -672,22 +672,24 @@ foreground and blue background." ;; Reverse the list so earlier entries take precedence (reverse notmuch-search-line-faces))) -(defun notmuch-search-author-propertize (authors) +(defun notmuch-search-author-propertize (authors matching-length) "Split `authors' into matching and non-matching authors and propertize appropriately. If no boundary between authors and non-authors is found, assume that all of the authors match." - (if (string-match "\\(.*\\)|\\(.*\\)" authors) - (concat (propertize (concat (match-string 1 authors) ",") - 'face 'notmuch-search-matching-authors) - (propertize (match-string 2 authors) - 'face 'notmuch-search-non-matching-authors)) -(propertize authors 'face 'notmuch-search-matching-authors))) - -(defun notmuch-search-insert-authors (format-string authors) + (let ((match-part (substring authors 0 matching-length)) + (non-match-part (substring authors matching-length))) + + (concat (propertize match-part 'face 'notmuch-search-matching-authors) + (propertize non-match-part 'face 'notmuch-search-non-matching-authors + +(defun notmuch-search-insert-authors (format-string matching-authors non-matching-authors) ;; Save the match data to avoid interfering with ;; `notmuch-search-process-filter'. (save-match-data -(let* ((formatted-authors (format format-string authors)) +(let* ((authors (if (string= "" non-matching-authors) + matching-authors + (concat matching-authors ", " non-matching-authors))) + (formatted-authors (format format-string authors)) (formatted-sample (format format-string "")) (visible-string formatted-authors) (invisible-string "") @@ -703,9 +705,9 @@ non-authors is found, assume that all of the authors match." (setq visible-string (substring formatted-authors 0 visible-length) invisible-string (substring formatted-authors visible-length)) ;; If possible, truncate the visible string at a natural - ;; break (comma or pipe), as incremental search doesn't - ;; match across the visible/invisible border. - (when (string-match "\\(.*\\)\\([,|] \\)\\([^,|]*\\)" visible-string) + ;; break (comma), as incremental search doesn't match + ;; across the visible/invisible border. + (when (string-match "\\(.*\\)\\(, \\)\\([^,]*\\)" visible-string) ;; Second clause is destructive on `visible-string', so ;; order is important. (setq invisible-string (concat (match-string 3 visible-string) @@ -721,20 +723,23 @@ non-authors is found, assume that all of the authors match." ? ;; Use different faces to show matching and non-matching authors. - (if (string-match "\\(.*\\)|\\(.*\\)" visible-string) - ;; The visible string contains both matching and - ;; non-matching authors. - (setq visible-string (notmuch-search-author-propertize visible-string) - ;; The invisible string must contain only non-matching - ;; authors, as the visible-string contains both. - invisible-string (propertize invisible-string -'face 'notmuch-search-non-matching-authors)) - ;; The visible string contains only matching authors. - (setq visible-string (propertize visible-string -'face 'notmuch-search-matching-authors) - ;; The invisible string may contain both matching and - ;; non-matching authors. - invisible-string (notmuch-search-author-propertize invisible-string))) + (let ((visible-length (length visible-string)) + (matching-length (length matching-authors))) + + (if (> visible-length matching-length) + ;; The visible string contains both matching and + ;; non-matching authors. + (setq visible-string (notmuch-search-author-propertize visible-string matching-length) + ;; The invisible string must contain only non-matching + ;; authors, as the visible-string contains both. + invisible-string (propertize invisible-string + 'face
[PATCH v1 1/3] search: Seperately report matching and non-matching authors.
In addition to the :authors attribute of each search result, include :authors_matched and :authors_non_matched attributes. Both attributes are always included. If there are no non-matching authors, the :authors_non_matched attribute is set to the empty string. --- lib/notmuch.h| 34 lib/thread.cc| 60 +++- notmuch-search.c | 6 ++ 3 files changed, 82 insertions(+), 18 deletions(-) diff --git a/lib/notmuch.h b/lib/notmuch.h index dae0416..30ce6c3 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -993,6 +993,40 @@ const char * notmuch_thread_get_authors (notmuch_thread_t *thread); /** + * Get the matched authors of 'thread' as a UTF-8 string. + * + * The returned string is a comma-separated list of the names of the + * authors of mail messages in the query results that belong to this + * thread. + * + * Authors are ordered by date. + * + * The returned string belongs to 'thread' and as such, should not be + * modified by the caller and will only be valid for as long as the + * thread is valid, (which is until notmuch_thread_destroy or until + * the query from which it derived is destroyed). + */ +const char * +notmuch_thread_get_authors_matched (notmuch_thread_t *thread); + +/** + * Get the non-matched authors of 'thread' as a UTF-8 string. + * + * The returned string is a comma-separated list of the names of the + * authors of mail messages in the query results that belong to this + * thread. + * + * Authors are ordered by date. + * + * The returned string belongs to 'thread' and as such, should not be + * modified by the caller and will only be valid for as long as the + * thread is valid, (which is until notmuch_thread_destroy or until + * the query from which it derived is destroyed). + */ +const char * +notmuch_thread_get_authors_non_matched (notmuch_thread_t *thread); + +/** * Get the subject of 'thread' as a UTF-8 string. * * The subject is taken from the first message (according to the query diff --git a/lib/thread.cc b/lib/thread.cc index 8922403..b344875 100644 --- a/lib/thread.cc +++ b/lib/thread.cc @@ -33,6 +33,8 @@ struct visible _notmuch_thread { GHashTable *matched_authors_hash; GPtrArray *matched_authors_array; char *authors; +char *authors_matched; +char *authors_non_matched; GHashTable *tags; /* All messages, oldest first. */ @@ -112,10 +114,11 @@ _thread_add_matched_author (notmuch_thread_t *thread, g_ptr_array_add (thread->matched_authors_array, author_copy); } -/* Construct an authors string from matched_authors_array and - * authors_array. The string contains matched authors first, then - * non-matched authors (with the two groups separated by '|'). Within - * each group, authors are listed in date order. */ +/* Construct the authors_matched, authors_non_matched and authors + * strings from matched_authors_array and authors_array. The authors + * string contains matched authors first, then non-matched authors + * (with the two groups separated by '|'). Within each group, authors + * are listed in date order. */ static void _resolve_thread_authors_string (notmuch_thread_t *thread) { @@ -123,36 +126,43 @@ _resolve_thread_authors_string (notmuch_thread_t *thread) char *author; int first_non_matched_author = 1; -/* First, list all matched authors in date order. */ +/* List all matched authors in date order. */ for (i = 0; i < thread->matched_authors_array->len; i++) { author = (char *) g_ptr_array_index (thread->matched_authors_array, i); - if (thread->authors) - thread->authors = talloc_asprintf (thread, "%s, %s", - thread->authors, - author); - else - thread->authors = author; + if (thread->authors_matched) { + thread->authors_matched = talloc_asprintf (thread, "%s, %s", + thread->authors_matched, + author); + } else { + thread->authors_matched = author; + } } -/* Next, append any non-matched authors that haven't already appeared. */ +/* List any non-matched authors that haven't already appeared. */ for (i = 0; i < thread->authors_array->len; i++) { author = (char *) g_ptr_array_index (thread->authors_array, i); if (g_hash_table_lookup_extended (thread->matched_authors_hash, author, NULL, NULL)) continue; if (first_non_matched_author) { - thread->authors = talloc_asprintf (thread, "%s| %s", - thread->authors, - author); + thread->authors_non_matched = author; } else { - thread->authors = talloc_asprintf (thread, "%s, %s", -
[PATCH v1 0/3] Improve the display of matching/non-matching authors.
Improve the display of matching/non-matching authors. Distinguishing between matching and non-matching authors in the emacs interface is currently done by parsing the :authors attribute of a search result. If one of the authors uses the pipe symbol (|) in their 'From' address this parsing incorrectly determines the matching and non-matching authors. Address this by adding explicit matching and non-matching authors attributes to the structured output formats. David Edmondson (3): search: Seperately report matching and non-matching authors. emacs: Improved display of matching/non-matching authors. test: Update tests for :authors_matched and :authors_non_matched. emacs/notmuch.el | 64 lib/notmuch.h| 34 +++ lib/thread.cc| 60 - notmuch-search.c | 6 + test/T160-json.sh| 4 +++ test/T170-sexp.sh| 4 +-- test/T470-missing-headers.sh | 4 +++ 7 files changed, 127 insertions(+), 49 deletions(-) -- 2.1.1
[PATCH v4 2/2] VIM: Add URI handling
Add URI handling to the vim client. You can now press 'enter' by default and the client will parse the current line and find any 'Part's or URIs available for opening. If there are more than one it opens the one under the cursor or else it opens the only one available. It also supports mailto: URI's and will compose a new message when activated. By default xdg-open is used for everything but mailto: which generally does the right thing afaict. Note that this is now dependant on the attachment patch in order to make the nice 'enter' behavior work for both. Ian --- Fixed commit message. vim/notmuch.txt | 3 ++- vim/notmuch.vim | 76 +++-- 2 files changed, 70 insertions(+), 9 deletions(-) diff --git a/vim/notmuch.txt b/vim/notmuch.txt index d5e1ad2..f51b20f 100644 --- a/vim/notmuch.txt +++ b/vim/notmuch.txt @@ -74,7 +74,8 @@ I Mark as read (-unread) t Tag (prompted) e Extract attachment on the current 'Part' line or all attachments if the cursor is elsewhere. - View attachment on the current 'Part' line. + View email part on the current 'Part' line, or open URI under cursor +or on line. s Search p Save patches r Reply diff --git a/vim/notmuch.vim b/vim/notmuch.vim index 1466e50..2f76f55 100644 --- a/vim/notmuch.vim +++ b/vim/notmuch.vim @@ -12,7 +12,7 @@ let g:notmuch_folders_maps = { \ '':'folders_show_search()', \ 's': 'folders_search_prompt()', \ '=': 'folders_refresh()', - \ 'c': 'compose()', + \ 'c': 'compose("")', \ } let g:notmuch_search_maps = { @@ -25,7 +25,7 @@ let g:notmuch_search_maps = { \ 's': 'search_search_prompt()', \ '=': 'search_refresh()', \ '?': 'search_info()', - \ 'c': 'compose()', + \ 'c': 'compose("")', \ } let g:notmuch_show_maps = { @@ -35,13 +35,13 @@ let g:notmuch_show_maps = { \ 't': 'show_tag("")', \ 'o': 'show_open_msg()', \ 'e': 'show_extract_msg()', - \ '':'show_view_attachment()', + \ '':'show_view_magic()', \ 's': 'show_save_msg()', \ 'p': 'show_save_patches()', \ 'r': 'show_reply()', \ '?': 'show_info()', \ '': 'show_next_msg()', - \ 'c': 'compose()', + \ 'c': 'compose("")', \ } let g:notmuch_compose_maps = { @@ -63,6 +63,7 @@ let s:notmuch_view_attachment_default = 'xdg-open' let s:notmuch_attachment_tmpdir_default = '~/.notmuch/tmp' let s:notmuch_folders_count_threads_default = 0 let s:notmuch_compose_start_insert_default = 1 +let s:notmuch_open_uri_default = 'xdg-open' function! s:new_file_buffer(type, fname) exec printf('edit %s', a:fname) @@ -141,8 +142,8 @@ function! s:show_reply() end endfunction -function! s:compose() - ruby open_compose +function! s:compose(to_email) + ruby open_compose(VIM::evaluate('a:to_email')) let b:compose_done = 0 call s:set_map(g:notmuch_compose_maps) autocmd BufDelete call s:on_compose_delete() @@ -155,6 +156,22 @@ function! s:show_info() ruby vim_puts get_message.inspect endfunction +function! s:show_view_magic() + let line = getline(".") + +ruby << EOF + line = VIM::evaluate('line') + + # Easiest to check for 'Part' types first.. + match = line.match(/^Part (\d*):/) + if match and match.length == 2 + VIM::command('call s:show_view_attachment()') + else + VIM::command('call s:show_open_uri()') + end +EOF +endfunction + function! s:show_view_attachment() let line = getline(".") ruby << EOF @@ -226,6 +243,45 @@ ruby << EOF EOF endfunction +function! s:show_open_uri() + let line = getline(".") + let pos = getpos(".") + let col = pos[2] +ruby << EOF + m = get_message + line = VIM::evaluate('line') + col = VIM::evaluate('col') - 1 + uris = URI.extract(line) + wanted_uri = nil + if uris.length == 1 + wanted_uri = uris[0] + else + uris.each do |uri| + # Check to see the URI is at the present cursor location + idx = line.index(uri) + if col >= idx and col <= idx + uri.length + wanted_uri = uri + break + end + end + end + + if wanted_uri + uri = URI.parse(wanted_uri) + if uri.class == URI::MailTo + vim_puts("Composing new email to #{uri.to}.") + VIM::command("call s:compose('#{uri.to}')") + else + vim_puts("Opening #{uri.to_s}.") +
[PATCH v4 1/2] VIM: Add better attachment support
Change how the notmuch vim client supports attachments: - For each message part a 'Part : ' is added to the header. - You can then use 'e' to extract the attachment under the cursor or use it elsewhere to extract all attachments (the prior behavior) - You can use 'v' to 'view' the attachment/part using xdg-open by default. - If the message is 'text/html' we include a 'Part:' for the body of the message so you can easily view it in a web browser if you so choose. Ian --- - Fixed commit message - Fixed documentation vim/notmuch.txt | 8 +- vim/notmuch.vim | 84 +++-- 2 files changed, 89 insertions(+), 3 deletions(-) diff --git a/vim/notmuch.txt b/vim/notmuch.txt index 4374102..d5e1ad2 100644 --- a/vim/notmuch.txt +++ b/vim/notmuch.txt @@ -72,6 +72,9 @@ q Quit view A Archive (-inbox -unread) I Mark as read (-unread) t Tag (prompted) +e Extract attachment on the current 'Part' line or all + attachments if the cursor is elsewhere. + View attachment on the current 'Part' line. s Search p Save patches r Reply @@ -148,6 +151,9 @@ You can also configure your externail mail reader and sendemail program: > let g:notmuch_reader = 'mutt -f %s' let g:notmuch_sendmail = 'sendmail' -< + +You can also configure what probram is used to view attachments: + + let g:notmuch_view_attachment = 'xdg-open' vim:tw=78:ts=8:noet:ft=help: diff --git a/vim/notmuch.vim b/vim/notmuch.vim index cad9517..1466e50 100644 --- a/vim/notmuch.vim +++ b/vim/notmuch.vim @@ -35,6 +35,7 @@ let g:notmuch_show_maps = { \ 't': 'show_tag("")', \ 'o': 'show_open_msg()', \ 'e': 'show_extract_msg()', + \ '':'show_view_attachment()', \ 's': 'show_save_msg()', \ 'p': 'show_save_patches()', \ 'r': 'show_reply()', @@ -58,6 +59,8 @@ let s:notmuch_date_format_default = '%d.%m.%y' let s:notmuch_datetime_format_default = '%d.%m.%y %H:%M:%S' let s:notmuch_reader_default = 'mutt -f %s' let s:notmuch_sendmail_default = 'sendmail' +let s:notmuch_view_attachment_default = 'xdg-open' +let s:notmuch_attachment_tmpdir_default = '~/.notmuch/tmp' let s:notmuch_folders_count_threads_default = 0 let s:notmuch_compose_start_insert_default = 1 @@ -152,13 +155,72 @@ function! s:show_info() ruby vim_puts get_message.inspect endfunction +function! s:show_view_attachment() + let line = getline(".") +ruby << EOF + m = get_message + line = VIM::evaluate('line') + + match = line.match(/^Part (\d*):/) + if match and match.length == 2 + # Set up the tmpdir + tmpdir = VIM::evaluate('g:notmuch_attachment_tmpdir') + tmpdir = File.expand_path(tmpdir) + Dir.mkdir(tmpdir) unless Dir.exists?(tmpdir) + + p = m.mail.parts[match[1].to_i - 1] + if p == nil + # Not a multipart message, use the message itself. + p = m.mail + end + if p.filename and p.filename.length > 0 + filename = p.filename + else + suffix = '' + if p.mime_type == 'text/html' + suffix = '.html' + end + filename = "part-#{match[1]}#{suffix}" + end + + # Sanitize just in case.. + filename.gsub!(/[^0-9A-Za-z.\-]/, '_') + + fullpath = File.expand_path("#{tmpdir}/#{filename}") + vim_puts "Viewing attachment #{fullpath}" + File.open(fullpath, 'w') do |f| + f.write p.body.decoded + cmd = VIM::evaluate('g:notmuch_view_attachment') + system(cmd, fullpath) + end + else + vim_puts "No attachment on this line." + end +EOF +endfunction + function! s:show_extract_msg() + let line = getline(".") ruby << EOF m = get_message - m.mail.attachments.each do |a| + line = VIM::evaluate('line') + + # If the user is on a line that has an 'Part' + # line, we just extract the one attachment. + match = line.match(/^Part (\d*):/) + if match and match.length == 2 + a = m.mail.parts[match[1].to_i - 1] File.open(a.filename, 'w') do |f| f.write a.body.decoded - print "Extracted '#{a.filename}'" + vim_puts "Extracted #{a.filename}" + end + else + # Extract them all.. + m.mail.attachments.each do |a| + File.open(a.filename, 'w') do |f| + f.write a.body.decoded + vim_puts "Extracted
[PATCH v3] VIM: Add URI handling
This patch adds URI handling to the vim client. You can now press 'enter' by default and the client will parse the current line and find any 'Part's or URIs available for opening. If there are more than one it opens the one under the cursor or else it opens the only one available. It also supports mailto: URI's and will compose a new message when activated. By default xdg-open is used for everything but mailto: which generally does the right thing afaict. Note that this is now dependant on the attachment patch in order to make the nice 'enter' behavior work for both. Ian --- Fix commit message formatting. vim/notmuch.txt | 3 ++- vim/notmuch.vim | 76 +++-- 2 files changed, 70 insertions(+), 9 deletions(-) diff --git a/vim/notmuch.txt b/vim/notmuch.txt index 838a904..5d84fde 100644 --- a/vim/notmuch.txt +++ b/vim/notmuch.txt @@ -74,7 +74,8 @@ I Mark as read (-unread) t Tag (prompted) e Extract attachment on the current 'Attachment' line or all attachments if the cursor is elsewhere. -v View attachment on the current 'Attachment' line. + View email part on the current 'Part' line, or open URI under cursor +or on line. s Search p Save patches r Reply diff --git a/vim/notmuch.vim b/vim/notmuch.vim index 1466e50..2f76f55 100644 --- a/vim/notmuch.vim +++ b/vim/notmuch.vim @@ -12,7 +12,7 @@ let g:notmuch_folders_maps = { \ '':'folders_show_search()', \ 's': 'folders_search_prompt()', \ '=': 'folders_refresh()', - \ 'c': 'compose()', + \ 'c': 'compose("")', \ } let g:notmuch_search_maps = { @@ -25,7 +25,7 @@ let g:notmuch_search_maps = { \ 's': 'search_search_prompt()', \ '=': 'search_refresh()', \ '?': 'search_info()', - \ 'c': 'compose()', + \ 'c': 'compose("")', \ } let g:notmuch_show_maps = { @@ -35,13 +35,13 @@ let g:notmuch_show_maps = { \ 't': 'show_tag("")', \ 'o': 'show_open_msg()', \ 'e': 'show_extract_msg()', - \ '':'show_view_attachment()', + \ '':'show_view_magic()', \ 's': 'show_save_msg()', \ 'p': 'show_save_patches()', \ 'r': 'show_reply()', \ '?': 'show_info()', \ '': 'show_next_msg()', - \ 'c': 'compose()', + \ 'c': 'compose("")', \ } let g:notmuch_compose_maps = { @@ -63,6 +63,7 @@ let s:notmuch_view_attachment_default = 'xdg-open' let s:notmuch_attachment_tmpdir_default = '~/.notmuch/tmp' let s:notmuch_folders_count_threads_default = 0 let s:notmuch_compose_start_insert_default = 1 +let s:notmuch_open_uri_default = 'xdg-open' function! s:new_file_buffer(type, fname) exec printf('edit %s', a:fname) @@ -141,8 +142,8 @@ function! s:show_reply() end endfunction -function! s:compose() - ruby open_compose +function! s:compose(to_email) + ruby open_compose(VIM::evaluate('a:to_email')) let b:compose_done = 0 call s:set_map(g:notmuch_compose_maps) autocmd BufDelete call s:on_compose_delete() @@ -155,6 +156,22 @@ function! s:show_info() ruby vim_puts get_message.inspect endfunction +function! s:show_view_magic() + let line = getline(".") + +ruby << EOF + line = VIM::evaluate('line') + + # Easiest to check for 'Part' types first.. + match = line.match(/^Part (\d*):/) + if match and match.length == 2 + VIM::command('call s:show_view_attachment()') + else + VIM::command('call s:show_open_uri()') + end +EOF +endfunction + function! s:show_view_attachment() let line = getline(".") ruby << EOF @@ -226,6 +243,45 @@ ruby << EOF EOF endfunction +function! s:show_open_uri() + let line = getline(".") + let pos = getpos(".") + let col = pos[2] +ruby << EOF + m = get_message + line = VIM::evaluate('line') + col = VIM::evaluate('col') - 1 + uris = URI.extract(line) + wanted_uri = nil + if uris.length == 1 + wanted_uri = uris[0] + else + uris.each do |uri| + # Check to see the URI is at the present cursor location + idx = line.index(uri) + if col >= idx and col <= idx + uri.length + wanted_uri = uri + break + end + end + end + + if wanted_uri + uri = URI.parse(wanted_uri) + if uri.class == URI::MailTo + vim_puts("Composing new email to #{uri.to}.") + VIM::command("call s:compose('#{uri.to}')") + else + vim_puts("Opening
Looking for the perfect mail client
Sepp Tannhuber wrote: > Ian Main schrieb am 6:28 Freitag, 24.Oktober 2014: > > > I wonder if you'd be willing to give the vim client a go. We've done a lot > > of work to it lately and I think it would handle all you listed here. > For me, it's hard to get familiar with it because I have not found a detailed > documentation. The README file is not good enough for a stupid person. Interesting, good feedback. Is it the usage that is difficult or setup? > I also doubt that it can render html properly. Well like any other text based client it uses a text dump of a links or such (I forget which one). We have great support for web browser viewing of the email though if that isn't good enough. > Nevertheless, I would give it a try if I could find useful documentation. Let me know if you have suggestions on where it's lacking. We need a better installation guide plus a tutorial maybe? Ian
[PATCH v3.1 3/9] lib: Introduce macros for bit operations
These macros help clarify basic bit-twiddling code and are written to be robust against C undefined behavior of shift operators. --- lib/message.cc| 6 +++--- lib/notmuch-private.h | 11 +++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/lib/message.cc b/lib/message.cc index 38bc929..55d2ff6 100644 --- a/lib/message.cc +++ b/lib/message.cc @@ -869,7 +869,7 @@ notmuch_bool_t notmuch_message_get_flag (notmuch_message_t *message, notmuch_message_flag_t flag) { -return message->flags & (1 << flag); +return NOTMUCH_TEST_BIT (message->flags, flag); } void @@ -877,9 +877,9 @@ notmuch_message_set_flag (notmuch_message_t *message, notmuch_message_flag_t flag, notmuch_bool_t enable) { if (enable) - message->flags |= (1 << flag); + NOTMUCH_SET_BIT (>flags, flag); else - message->flags &= ~(1 << flag); + NOTMUCH_CLEAR_BIT (>flags, flag); } time_t diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h index 36cc12b..b86897c 100644 --- a/lib/notmuch-private.h +++ b/lib/notmuch-private.h @@ -63,6 +63,17 @@ NOTMUCH_BEGIN_DECLS #define STRNCMP_LITERAL(var, literal) \ strncmp ((var), (literal), sizeof (literal) - 1) +/* Robust bit test/set/reset macros */ +#define NOTMUCH_TEST_BIT(val, bit) \ +(((bit) < 0 || (bit) >= CHAR_BIT * sizeof (unsigned long long)) ? 0 \ + : !!((val) & (1ull << (bit +#define NOTMUCH_SET_BIT(valp, bit) \ +(((bit) < 0 || (bit) >= CHAR_BIT * sizeof (unsigned long long)) ? *(valp) \ + : (*(valp) |= (1ull << (bit +#define NOTMUCH_CLEAR_BIT(valp, bit) \ +(((bit) < 0 || (bit) >= CHAR_BIT * sizeof (unsigned long long)) ? *(valp) \ + : (*(valp) &= ~(1ull << (bit + #define unused(x) x __attribute__ ((unused)) #ifdef __cplusplus -- 2.1.0
[PATCH v1 1/3] search: Seperately report matching and non-matching authors.
Quoth Mark Walters on Oct 24 at 10:23 am: > > Hi > > I definitely like the idea: some comments below. Agreed. > On Fri, 24 Oct 2014, David Edmondson wrote: > > In addition to the :authors attribute of each search result, include > > :authors_matched and :authors_non_matched attributes. Both attributes > > are always included. If there are no non-matching authors, the > > :authors_non_matched attribute is set to the empty string. > > What about having both authors_matched and authors_not_matched as lists > of authors (ie one string for each author)? Then emacs, for example, > wouldn't try and parse the string back into authors before > splitting. And authors_non_matched could be an empty list when > appropriate which seems more natural than the empty string. > > All the above is based on what a client might want in the output rather > than what is easy or sensible to implement in the C code. I was going to suggest exactly the same thing. And I think there's a fairly easy way to do it in C code that will also prevent library interface bloat: instead of introducing new library APIs to get at this information, just use the existing notmuch_thread_get_messages API and construct the matched and non-matched lists in the CLI. Doing it in the CLI wouldn't require the library to export yet another string list structure, which is always a huge pain (thanks C!), and wouldn't introduce more "helper" functions into the library API. I think the only disadvantage to doing this would be some duplication of the {matched_,}authors_{array,hash} logic into the CLI, but those are only there to support notmuch_thread_get_authors, which I think is a huge hack and should go away in the long term. (And, by doing this list building in the CLI, we avoid further embellishing the unnecessary "get thread authors" API).
[PATCH] VIM v2: Add a 'tag all' folder option.
This one is pretty straightforward and useful too. Ian Ian Main wrote: > This adds the ability to mark an entire folder as read (or any other > tags you like once you map it). > > This update adds documentation for the command. > > Ian > --- > vim/notmuch.txt | 1 + > vim/notmuch.vim | 11 +++ > 2 files changed, 12 insertions(+) > > diff --git a/vim/notmuch.txt b/vim/notmuch.txt > index 4374102..33cbe6e 100644 > --- a/vim/notmuch.txt > +++ b/vim/notmuch.txt > @@ -47,6 +47,7 @@ MAPPINGS > *notmuch-mappings* > Folder view~ > >Show selected search > +AArchive (-inbox -unread) an entire folder > sEnter a new search > =Refresh > cCompose a new mail > diff --git a/vim/notmuch.vim b/vim/notmuch.vim > index 331e930..3f2444b 100644 > --- a/vim/notmuch.vim > +++ b/vim/notmuch.vim > @@ -11,6 +11,7 @@ let g:loaded_notmuch = "yep" > let g:notmuch_folders_maps = { > \ '':'folders_show_search()', > \ 's': 'folders_search_prompt()', > + \ 'A': 'folders_tag_all("-inbox -unread")', > \ '=': 'folders_refresh()', > \ 'c': 'compose()', > \ } > @@ -378,6 +379,16 @@ ruby << EOF > EOF > endfunction > > +function! s:folders_tag_all(tags) > +ruby << EOF > + n = $curbuf.line_number > + s = $searches[n - 1] > + t = VIM::evaluate('a:tags') > + do_tag(s, t) > +EOF > + call s:folders_refresh() > +endfunction > + > function! s:folders() > call s:new_buffer('folders') > ruby folders_render() > -- > 1.9.3 >
[PATCH] VIM: Automatically refresh folder screen
Can I get someone to test this one? It's pretty simple and useful. Ian Ian Main wrote: > This patch makes the folder screen refresh each time you 'enter' it. > This way when you read a folder and mark items as read the changes are > reflected immediately when you return to the folder view. > > Ian > --- > vim/notmuch.vim | 3 +++ > 1 file changed, 3 insertions(+) > > diff --git a/vim/notmuch.vim b/vim/notmuch.vim > index 331e930..eb17d57 100644 > --- a/vim/notmuch.vim > +++ b/vim/notmuch.vim > @@ -383,6 +383,9 @@ function! s:folders() > ruby folders_render() > call s:set_menu_buffer() > call s:set_map(g:notmuch_folders_maps) > + autocmd BufEnter,WinEnter,BufWinEnter > + \ call s:folders_refresh() > + augroup END > endfunction > > "" root > -- > 1.9.3 >
RE: [PATCH] VIM: Automatically refresh folder screen
Can I get someone to test this one? It's pretty simple and useful. Ian Ian Main wrote: This patch makes the folder screen refresh each time you 'enter' it. This way when you read a folder and mark items as read the changes are reflected immediately when you return to the folder view. Ian --- vim/notmuch.vim | 3 +++ 1 file changed, 3 insertions(+) diff --git a/vim/notmuch.vim b/vim/notmuch.vim index 331e930..eb17d57 100644 --- a/vim/notmuch.vim +++ b/vim/notmuch.vim @@ -383,6 +383,9 @@ function! s:folders() ruby folders_render() call s:set_menu_buffer() call s:set_map(g:notmuch_folders_maps) + autocmd BufEnter,WinEnter,BufWinEnter buffer + \ call s:folders_refresh() + augroup END endfunction root -- 1.9.3 ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
RE: [PATCH] VIM v2: Add a 'tag all' folder option.
This one is pretty straightforward and useful too. Ian Ian Main wrote: This adds the ability to mark an entire folder as read (or any other tags you like once you map it). This update adds documentation for the command. Ian --- vim/notmuch.txt | 1 + vim/notmuch.vim | 11 +++ 2 files changed, 12 insertions(+) diff --git a/vim/notmuch.txt b/vim/notmuch.txt index 4374102..33cbe6e 100644 --- a/vim/notmuch.txt +++ b/vim/notmuch.txt @@ -47,6 +47,7 @@ MAPPINGS *notmuch-mappings* Folder view~ enter Show selected search +AArchive (-inbox -unread) an entire folder sEnter a new search =Refresh cCompose a new mail diff --git a/vim/notmuch.vim b/vim/notmuch.vim index 331e930..3f2444b 100644 --- a/vim/notmuch.vim +++ b/vim/notmuch.vim @@ -11,6 +11,7 @@ let g:loaded_notmuch = yep let g:notmuch_folders_maps = { \ 'Enter':'folders_show_search()', \ 's': 'folders_search_prompt()', + \ 'A': 'folders_tag_all(-inbox -unread)', \ '=': 'folders_refresh()', \ 'c': 'compose()', \ } @@ -378,6 +379,16 @@ ruby EOF EOF endfunction +function! s:folders_tag_all(tags) +ruby EOF + n = $curbuf.line_number + s = $searches[n - 1] + t = VIM::evaluate('a:tags') + do_tag(s, t) +EOF + call s:folders_refresh() +endfunction + function! s:folders() call s:new_buffer('folders') ruby folders_render() -- 1.9.3 ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
[PATCH] doc: add README.md especially for github
Apparently README.md overrides README, so this will show up instead of our generic README on github. If the user is already on github, then clicking a link for more information is not a hardship. --- README.md | 15 +++ 1 file changed, 15 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 000..ce579d8 --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +If you're reading this on github.com, this is a read-only mirror of +the notmuch project. + +For more information about the project, see + + http://notmuchmail.org + +Please don't send us pull requests on github. If you have a feature +branch that you want us to look at, use `git send-email` to send to +notmuch@notmuchmail.org. + +For more information about contributing to the project, see + + + http://notmuchmail.org/contributing/ -- 2.1.1 ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v1 1/3] search: Seperately report matching and non-matching authors.
In addition to the :authors attribute of each search result, include :authors_matched and :authors_non_matched attributes. Both attributes are always included. If there are no non-matching authors, the :authors_non_matched attribute is set to the empty string. --- lib/notmuch.h| 34 lib/thread.cc| 60 +++- notmuch-search.c | 6 ++ 3 files changed, 82 insertions(+), 18 deletions(-) diff --git a/lib/notmuch.h b/lib/notmuch.h index dae0416..30ce6c3 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -993,6 +993,40 @@ const char * notmuch_thread_get_authors (notmuch_thread_t *thread); /** + * Get the matched authors of 'thread' as a UTF-8 string. + * + * The returned string is a comma-separated list of the names of the + * authors of mail messages in the query results that belong to this + * thread. + * + * Authors are ordered by date. + * + * The returned string belongs to 'thread' and as such, should not be + * modified by the caller and will only be valid for as long as the + * thread is valid, (which is until notmuch_thread_destroy or until + * the query from which it derived is destroyed). + */ +const char * +notmuch_thread_get_authors_matched (notmuch_thread_t *thread); + +/** + * Get the non-matched authors of 'thread' as a UTF-8 string. + * + * The returned string is a comma-separated list of the names of the + * authors of mail messages in the query results that belong to this + * thread. + * + * Authors are ordered by date. + * + * The returned string belongs to 'thread' and as such, should not be + * modified by the caller and will only be valid for as long as the + * thread is valid, (which is until notmuch_thread_destroy or until + * the query from which it derived is destroyed). + */ +const char * +notmuch_thread_get_authors_non_matched (notmuch_thread_t *thread); + +/** * Get the subject of 'thread' as a UTF-8 string. * * The subject is taken from the first message (according to the query diff --git a/lib/thread.cc b/lib/thread.cc index 8922403..b344875 100644 --- a/lib/thread.cc +++ b/lib/thread.cc @@ -33,6 +33,8 @@ struct visible _notmuch_thread { GHashTable *matched_authors_hash; GPtrArray *matched_authors_array; char *authors; +char *authors_matched; +char *authors_non_matched; GHashTable *tags; /* All messages, oldest first. */ @@ -112,10 +114,11 @@ _thread_add_matched_author (notmuch_thread_t *thread, g_ptr_array_add (thread-matched_authors_array, author_copy); } -/* Construct an authors string from matched_authors_array and - * authors_array. The string contains matched authors first, then - * non-matched authors (with the two groups separated by '|'). Within - * each group, authors are listed in date order. */ +/* Construct the authors_matched, authors_non_matched and authors + * strings from matched_authors_array and authors_array. The authors + * string contains matched authors first, then non-matched authors + * (with the two groups separated by '|'). Within each group, authors + * are listed in date order. */ static void _resolve_thread_authors_string (notmuch_thread_t *thread) { @@ -123,36 +126,43 @@ _resolve_thread_authors_string (notmuch_thread_t *thread) char *author; int first_non_matched_author = 1; -/* First, list all matched authors in date order. */ +/* List all matched authors in date order. */ for (i = 0; i thread-matched_authors_array-len; i++) { author = (char *) g_ptr_array_index (thread-matched_authors_array, i); - if (thread-authors) - thread-authors = talloc_asprintf (thread, %s, %s, - thread-authors, - author); - else - thread-authors = author; + if (thread-authors_matched) { + thread-authors_matched = talloc_asprintf (thread, %s, %s, + thread-authors_matched, + author); + } else { + thread-authors_matched = author; + } } -/* Next, append any non-matched authors that haven't already appeared. */ +/* List any non-matched authors that haven't already appeared. */ for (i = 0; i thread-authors_array-len; i++) { author = (char *) g_ptr_array_index (thread-authors_array, i); if (g_hash_table_lookup_extended (thread-matched_authors_hash, author, NULL, NULL)) continue; if (first_non_matched_author) { - thread-authors = talloc_asprintf (thread, %s| %s, - thread-authors, - author); + thread-authors_non_matched = author; } else { - thread-authors = talloc_asprintf (thread, %s, %s, -
[PATCH v1 0/3] Improve the display of matching/non-matching authors.
Improve the display of matching/non-matching authors. Distinguishing between matching and non-matching authors in the emacs interface is currently done by parsing the :authors attribute of a search result. If one of the authors uses the pipe symbol (|) in their 'From' address this parsing incorrectly determines the matching and non-matching authors. Address this by adding explicit matching and non-matching authors attributes to the structured output formats. David Edmondson (3): search: Seperately report matching and non-matching authors. emacs: Improved display of matching/non-matching authors. test: Update tests for :authors_matched and :authors_non_matched. emacs/notmuch.el | 64 lib/notmuch.h| 34 +++ lib/thread.cc| 60 - notmuch-search.c | 6 + test/T160-json.sh| 4 +++ test/T170-sexp.sh| 4 +-- test/T470-missing-headers.sh | 4 +++ 7 files changed, 127 insertions(+), 49 deletions(-) -- 2.1.1 ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v1 2/3] emacs: Improved display of matching/non-matching authors.
Rather than splitting the :authors attribute, which is error prone, use the separate :authors_matched and :authors_non_matched attributes. This improves the display of authors should one of them include a pipe symbol (|) in their 'from' address. --- emacs/notmuch.el | 64 +++- 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/emacs/notmuch.el b/emacs/notmuch.el index b44a907..29b6cdc 100644 --- a/emacs/notmuch.el +++ b/emacs/notmuch.el @@ -672,22 +672,24 @@ foreground and blue background. ;; Reverse the list so earlier entries take precedence (reverse notmuch-search-line-faces))) -(defun notmuch-search-author-propertize (authors) +(defun notmuch-search-author-propertize (authors matching-length) Split `authors' into matching and non-matching authors and propertize appropriately. If no boundary between authors and non-authors is found, assume that all of the authors match. - (if (string-match \\(.*\\)|\\(.*\\) authors) - (concat (propertize (concat (match-string 1 authors) ,) - 'face 'notmuch-search-matching-authors) - (propertize (match-string 2 authors) - 'face 'notmuch-search-non-matching-authors)) -(propertize authors 'face 'notmuch-search-matching-authors))) - -(defun notmuch-search-insert-authors (format-string authors) + (let ((match-part (substring authors 0 matching-length)) + (non-match-part (substring authors matching-length))) + + (concat (propertize match-part 'face 'notmuch-search-matching-authors) + (propertize non-match-part 'face 'notmuch-search-non-matching-authors + +(defun notmuch-search-insert-authors (format-string matching-authors non-matching-authors) ;; Save the match data to avoid interfering with ;; `notmuch-search-process-filter'. (save-match-data -(let* ((formatted-authors (format format-string authors)) +(let* ((authors (if (string= non-matching-authors) + matching-authors + (concat matching-authors , non-matching-authors))) + (formatted-authors (format format-string authors)) (formatted-sample (format format-string )) (visible-string formatted-authors) (invisible-string ) @@ -703,9 +705,9 @@ non-authors is found, assume that all of the authors match. (setq visible-string (substring formatted-authors 0 visible-length) invisible-string (substring formatted-authors visible-length)) ;; If possible, truncate the visible string at a natural - ;; break (comma or pipe), as incremental search doesn't - ;; match across the visible/invisible border. - (when (string-match \\(.*\\)\\([,|] \\)\\([^,|]*\\) visible-string) + ;; break (comma), as incremental search doesn't match + ;; across the visible/invisible border. + (when (string-match \\(.*\\)\\(, \\)\\([^,]*\\) visible-string) ;; Second clause is destructive on `visible-string', so ;; order is important. (setq invisible-string (concat (match-string 3 visible-string) @@ -721,20 +723,23 @@ non-authors is found, assume that all of the authors match. ? ;; Use different faces to show matching and non-matching authors. - (if (string-match \\(.*\\)|\\(.*\\) visible-string) - ;; The visible string contains both matching and - ;; non-matching authors. - (setq visible-string (notmuch-search-author-propertize visible-string) - ;; The invisible string must contain only non-matching - ;; authors, as the visible-string contains both. - invisible-string (propertize invisible-string -'face 'notmuch-search-non-matching-authors)) - ;; The visible string contains only matching authors. - (setq visible-string (propertize visible-string -'face 'notmuch-search-matching-authors) - ;; The invisible string may contain both matching and - ;; non-matching authors. - invisible-string (notmuch-search-author-propertize invisible-string))) + (let ((visible-length (length visible-string)) + (matching-length (length matching-authors))) + + (if ( visible-length matching-length) + ;; The visible string contains both matching and + ;; non-matching authors. + (setq visible-string (notmuch-search-author-propertize visible-string matching-length) + ;; The invisible string must contain only non-matching + ;; authors, as the visible-string contains both. + invisible-string (propertize invisible-string + 'face 'notmuch-search-non-matching-authors))
Re: Looking for the perfect mail client
Ian Main im...@redhat.com schrieb am 6:28 Freitag, 24.Oktober 2014: I wonder if you'd be willing to give the vim client a go. We've done a lot of work to it lately and I think it would handle all you listed here. For me, it's hard to get familiar with it because I have not found a detailed documentation. The README file is not good enough for a stupid person. I also doubt that it can render html properly. Nevertheless, I would give it a try if I could find useful documentation. ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
Re: [PATCH] doc: add README.md especially for github
On Fri, Oct 24 2014, David Bremner da...@tethera.net wrote: Apparently README.md overrides README, so this will show up instead of our generic README on github. If the user is already on github, then clicking a link for more information is not a hardship. LGTM. although I'd prefer README.rst Tomi --- README.md | 15 +++ 1 file changed, 15 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 000..ce579d8 --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +If you're reading this on github.com, this is a read-only mirror of +the notmuch project. + +For more information about the project, see + + http://notmuchmail.org + +Please don't send us pull requests on github. If you have a feature +branch that you want us to look at, use `git send-email` to send to +notmuch@notmuchmail.org. + +For more information about contributing to the project, see + + + http://notmuchmail.org/contributing/ -- 2.1.1 ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
Re: [PATCH v1 1/3] search: Seperately report matching and non-matching authors.
Hi I definitely like the idea: some comments below. On Fri, 24 Oct 2014, David Edmondson d...@dme.org wrote: In addition to the :authors attribute of each search result, include :authors_matched and :authors_non_matched attributes. Both attributes are always included. If there are no non-matching authors, the :authors_non_matched attribute is set to the empty string. What about having both authors_matched and authors_not_matched as lists of authors (ie one string for each author)? Then emacs, for example, wouldn't try and parse the string back into authors before splitting. And authors_non_matched could be an empty list when appropriate which seems more natural than the empty string. All the above is based on what a client might want in the output rather than what is easy or sensible to implement in the C code. --- lib/notmuch.h| 34 lib/thread.cc| 60 +++- notmuch-search.c | 6 ++ 3 files changed, 82 insertions(+), 18 deletions(-) diff --git a/lib/notmuch.h b/lib/notmuch.h index dae0416..30ce6c3 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -993,6 +993,40 @@ const char * notmuch_thread_get_authors (notmuch_thread_t *thread); /** + * Get the matched authors of 'thread' as a UTF-8 string. + * + * The returned string is a comma-separated list of the names of the + * authors of mail messages in the query results that belong to this + * thread. + * + * Authors are ordered by date. + * + * The returned string belongs to 'thread' and as such, should not be + * modified by the caller and will only be valid for as long as the + * thread is valid, (which is until notmuch_thread_destroy or until + * the query from which it derived is destroyed). + */ +const char * +notmuch_thread_get_authors_matched (notmuch_thread_t *thread); + +/** + * Get the non-matched authors of 'thread' as a UTF-8 string. + * + * The returned string is a comma-separated list of the names of the + * authors of mail messages in the query results that belong to this + * thread. + * + * Authors are ordered by date. + * + * The returned string belongs to 'thread' and as such, should not be + * modified by the caller and will only be valid for as long as the + * thread is valid, (which is until notmuch_thread_destroy or until + * the query from which it derived is destroyed). + */ +const char * +notmuch_thread_get_authors_non_matched (notmuch_thread_t *thread); + +/** * Get the subject of 'thread' as a UTF-8 string. * * The subject is taken from the first message (according to the query diff --git a/lib/thread.cc b/lib/thread.cc index 8922403..b344875 100644 --- a/lib/thread.cc +++ b/lib/thread.cc @@ -33,6 +33,8 @@ struct visible _notmuch_thread { GHashTable *matched_authors_hash; GPtrArray *matched_authors_array; char *authors; +char *authors_matched; +char *authors_non_matched; GHashTable *tags; /* All messages, oldest first. */ @@ -112,10 +114,11 @@ _thread_add_matched_author (notmuch_thread_t *thread, g_ptr_array_add (thread-matched_authors_array, author_copy); } -/* Construct an authors string from matched_authors_array and - * authors_array. The string contains matched authors first, then - * non-matched authors (with the two groups separated by '|'). Within - * each group, authors are listed in date order. */ +/* Construct the authors_matched, authors_non_matched and authors + * strings from matched_authors_array and authors_array. The authors + * string contains matched authors first, then non-matched authors + * (with the two groups separated by '|'). Within each group, authors + * are listed in date order. */ static void _resolve_thread_authors_string (notmuch_thread_t *thread) { @@ -123,36 +126,43 @@ _resolve_thread_authors_string (notmuch_thread_t *thread) char *author; int first_non_matched_author = 1; -/* First, list all matched authors in date order. */ +/* List all matched authors in date order. */ for (i = 0; i thread-matched_authors_array-len; i++) { author = (char *) g_ptr_array_index (thread-matched_authors_array, i); - if (thread-authors) - thread-authors = talloc_asprintf (thread, %s, %s, -thread-authors, -author); - else - thread-authors = author; + if (thread-authors_matched) { + thread-authors_matched = talloc_asprintf (thread, %s, %s, +thread-authors_matched, +author); + } else { + thread-authors_matched = author; + } } -/* Next, append any non-matched authors that haven't already appeared. */ +/* List any non-matched authors that haven't already appeared. */
Re: [PATCH v3 3/4] cli: Extend the search command for --output={sender, recipients}
On Thu, Oct 23 2014, Mark Walters markwalters1...@gmail.com wrote: On Sun, 12 Oct 2014, Michal Sojka sojk...@fel.cvut.cz wrote: The new outputs allow printing senders, recipients or both of matching messages. The --output option is converted from keyword argument to flags argument, which means that the user can use --output=sender and --output=recipients simultaneously, to print both. Other combinations produce an error. ... +static void +print_address_list (const search_options_t *o, InternetAddressList *list) +{ +InternetAddress *address; +int i; + +for (i = 0; i internet_address_list_length (list); i++) { +address = internet_address_list_get_address (list, i); +if (INTERNET_ADDRESS_IS_GROUP (address)) { +InternetAddressGroup *group; +InternetAddressList *group_list; + +group = INTERNET_ADDRESS_GROUP (address); +group_list = internet_address_group_get_members (group); +if (group_list == NULL) +continue; + +print_address_list (o, group_list); +} else { +InternetAddressMailbox *mailbox; +const char *name; +const char *addr; +char *full_address; + +mailbox = INTERNET_ADDRESS_MAILBOX (address); + +name = internet_address_get_name (address); +addr = internet_address_mailbox_get_addr (mailbox); + +if (name *name) +full_address = talloc_asprintf (o-format, %s %s, name, addr); +else +full_address = talloc_strdup (o-format, addr); + +if (!full_address) { +fprintf (stderr, Error: out of memory\n); +break; +} +o-format-string (o-format, full_address); +o-format-separator (o-format); + +talloc_free (full_address); Thinking about this some more how about printing the name and address as a structured pair/map (at least for all cases except text/text0 output): something like (in JSON) [name: John Doe address: john@example.com] It seems wrong to me to go to the effort of separating them in the C and then combining them in the output. This could also help with the questions about uniqueness. If the client can get the data ready parsed into name/address then it can deal with much of the uniqueness itself. In that case client can also filter based on some substring, reducing the memory requirements... My preference would be for the default to print one line for each distinct full_address, and then any filter-by options to refine from there. Hmm, now I cannot decide whether this or just print out all addresses of messages, or do this distinct full_address output -- it looks like all other --output options prints unique lines, but there is potential of quite a lot of memory usage there... ... probably the memory usage is not problem there, OOM-killer eventually does it's job if necessary (!) (but machine may be slow (and trashing) for a while (just thinking out loud)) (!) but could we have general filter option for search to drop data before it is even considered for caching! -- maybe later ? One other advantage of structuring the output is that it is extensible: for example, at some later stage, we could include a count in the map allowing the client can pick the most popular variant. , and in this case notmuch cannot print any output until the full address list is gathered... :D Best wishes Mark Tomi +} +} +} + +static void +print_address_string (const search_options_t *o, const char *recipients) +{ +InternetAddressList *list; + +if (recipients == NULL) +return; + +list = internet_address_list_parse_string (recipients); +if (list == NULL) +return; + +print_address_list (o, list); +} + static int do_search_messages (search_options_t *o) { @@ -266,11 +330,29 @@ do_search_messages (search_options_t *o) notmuch_filenames_destroy( filenames ); -} else { /* output == OUTPUT_MESSAGES */ +} else if (o-output == OUTPUT_MESSAGES) { format-set_prefix (format, id); format-string (format, notmuch_message_get_message_id (message)); format-separator (format); +} else { +if (o-output OUTPUT_SENDER) { +const char *addrs; + +addrs = notmuch_message_get_header (message, from); +print_address_string (o, addrs); +} + +if (o-output OUTPUT_RECIPIENTS) { +const char *hdrs[] = { to, cc, bcc }; +const char *addrs; +size_t j; + +for (j = 0; j ARRAY_SIZE (hdrs); j++) { +addrs = notmuch_message_get_header (message, hdrs[j]); +print_address_string (o, addrs); +} +} } notmuch_message_destroy (message); @@ -337,7 +419,7 @@ notmuch_search_command (notmuch_config_t
Re: [PATCH v2] VIM: Add URI handling
On Fri, Oct 24 2014, Ian Main im...@stemwinder.org wrote: Add URI handling to the vim client. You can now press 'enter' by default and the client will parse the current line and find any 'Part's or URIs available for opening. If there are more than one it opens the one under the cursor or else it opens the only one available. It also supports mailto: URI's and will compose a new message when activated. The lines are way too long in this commit message. 72-chars except in cases where there is no word breaks in the text. id:1414101891-10714-1-git-send-email-im...@stemwinder.org has also one 75-character line (and talks about 'This patch ...' which could be changed too (but is tolerated if not)). Tomi By default xdg-open is used for everything but mailto: which generally does the right thing afaict. Note that this is now dependant on the attachment patch in order to make the nice 'enter' behavior work for both. Ian --- vim/notmuch.txt | 3 ++- vim/notmuch.vim | 76 +++-- 2 files changed, 70 insertions(+), 9 deletions(-) diff --git a/vim/notmuch.txt b/vim/notmuch.txt index 838a904..5d84fde 100644 --- a/vim/notmuch.txt +++ b/vim/notmuch.txt @@ -74,7 +74,8 @@ I Mark as read (-unread) tTag (prompted) e Extract attachment on the current 'Attachment' line or all attachments if the cursor is elsewhere. -v View attachment on the current 'Attachment' line. +enter View email part on the current 'Part' line, or open URI under cursor +or on line. sSearch pSave patches rReply diff --git a/vim/notmuch.vim b/vim/notmuch.vim index 1466e50..2f76f55 100644 --- a/vim/notmuch.vim +++ b/vim/notmuch.vim @@ -12,7 +12,7 @@ let g:notmuch_folders_maps = { \ 'Enter':'folders_show_search()', \ 's': 'folders_search_prompt()', \ '=': 'folders_refresh()', - \ 'c': 'compose()', + \ 'c': 'compose()', \ } let g:notmuch_search_maps = { @@ -25,7 +25,7 @@ let g:notmuch_search_maps = { \ 's': 'search_search_prompt()', \ '=': 'search_refresh()', \ '?': 'search_info()', - \ 'c': 'compose()', + \ 'c': 'compose()', \ } let g:notmuch_show_maps = { @@ -35,13 +35,13 @@ let g:notmuch_show_maps = { \ 't': 'show_tag()', \ 'o': 'show_open_msg()', \ 'e': 'show_extract_msg()', - \ 'Enter':'show_view_attachment()', + \ 'Enter':'show_view_magic()', \ 's': 'show_save_msg()', \ 'p': 'show_save_patches()', \ 'r': 'show_reply()', \ '?': 'show_info()', \ 'Tab': 'show_next_msg()', - \ 'c': 'compose()', + \ 'c': 'compose()', \ } let g:notmuch_compose_maps = { @@ -63,6 +63,7 @@ let s:notmuch_view_attachment_default = 'xdg-open' let s:notmuch_attachment_tmpdir_default = '~/.notmuch/tmp' let s:notmuch_folders_count_threads_default = 0 let s:notmuch_compose_start_insert_default = 1 +let s:notmuch_open_uri_default = 'xdg-open' function! s:new_file_buffer(type, fname) exec printf('edit %s', a:fname) @@ -141,8 +142,8 @@ function! s:show_reply() end endfunction -function! s:compose() - ruby open_compose +function! s:compose(to_email) + ruby open_compose(VIM::evaluate('a:to_email')) let b:compose_done = 0 call s:set_map(g:notmuch_compose_maps) autocmd BufDelete buffer call s:on_compose_delete() @@ -155,6 +156,22 @@ function! s:show_info() ruby vim_puts get_message.inspect endfunction +function! s:show_view_magic() + let line = getline(.) + +ruby EOF + line = VIM::evaluate('line') + + # Easiest to check for 'Part' types first.. + match = line.match(/^Part (\d*):/) + if match and match.length == 2 + VIM::command('call s:show_view_attachment()') + else + VIM::command('call s:show_open_uri()') + end +EOF +endfunction + function! s:show_view_attachment() let line = getline(.) ruby EOF @@ -226,6 +243,45 @@ ruby EOF EOF endfunction +function! s:show_open_uri() + let line = getline(.) + let pos = getpos(.) + let col = pos[2] +ruby EOF + m = get_message + line = VIM::evaluate('line') + col = VIM::evaluate('col') - 1 + uris = URI.extract(line) + wanted_uri = nil + if uris.length == 1 + wanted_uri = uris[0] + else + uris.each do |uri| + # Check to see the URI is at the present cursor location + idx = line.index(uri) + if col = idx and col = idx + uri.length + wanted_uri = uri + break + end +
Re: [PATCH] doc: add README.md especially for github
Tomi Ollila tomi.oll...@iki.fi writes: On Fri, Oct 24 2014, David Bremner da...@tethera.net wrote: Apparently README.md overrides README, so this will show up instead of our generic README on github. If the user is already on github, then clicking a link for more information is not a hardship. LGTM. although I'd prefer README.rst OK, rst version pushed and shows up on github. d ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
Re: [PATCH v3 3/4] cli: Extend the search command for --output={sender, recipients}
On Thu, Oct 23 2014, Mark Walters wrote: Thinking about this some more how about printing the name and address as a structured pair/map (at least for all cases except text/text0 output): something like (in JSON) [name: John Doe address: john@example.com] It seems wrong to me to go to the effort of separating them in the C and then combining them in the output. Agreed, this would be convenient. ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
Re: [PATCH v1 1/3] search: Seperately report matching and non-matching authors.
On Fri, Oct 24 2014, Mark Walters wrote: What about having both authors_matched and authors_not_matched as lists of authors (ie one string for each author)? That's a sensible idea. I will look into it. Then emacs, for example, wouldn't try and parse the string back into authors before splitting. It doesn't really do that other than to decide where to truncate the visible string. That whole chunk of code is horrible. first_non_matched_author = 0; I think I would prefer to make this look like the matched case and drop the first_non_matched_author stuff. Agreed, will do. ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
Re: [PATCH v3 3/4] cli: Extend the search command for --output={sender, recipients}
Hi Mark, I mostly agree with your points mentioned in this and other your emails. I'll prepare v4 based on that. On Wed, Oct 22 2014, Mark Walters wrote: On Sun, 12 Oct 2014, Michal Sojka sojk...@fel.cvut.cz wrote: The new outputs allow printing senders, recipients or both of matching messages. The --output option is converted from keyword argument to flags argument, which means that the user can use --output=sender and --output=recipients simultaneously, to print both. Other combinations produce an error. This code based on a patch from Jani Nikula. --- completion/notmuch-completion.bash | 2 +- completion/notmuch-completion.zsh | 3 +- doc/man1/notmuch-search.rst| 22 +++- notmuch-search.c | 110 ++--- test/T090-search-output.sh | 64 + 5 files changed, 189 insertions(+), 12 deletions(-) diff --git a/completion/notmuch-completion.bash b/completion/notmuch-completion.bash index 0571dc9..cfbd389 100644 --- a/completion/notmuch-completion.bash +++ b/completion/notmuch-completion.bash @@ -294,7 +294,7 @@ _notmuch_search() return ;; --output) -COMPREPLY=( $( compgen -W summary threads messages files tags -- ${cur} ) ) +COMPREPLY=( $( compgen -W summary threads messages files tags sender recipients -- ${cur} ) ) return ;; --sort) diff --git a/completion/notmuch-completion.zsh b/completion/notmuch-completion.zsh index 67a9aba..3e52a00 100644 --- a/completion/notmuch-completion.zsh +++ b/completion/notmuch-completion.zsh @@ -52,7 +52,8 @@ _notmuch_search() _arguments -s : \ '--max-threads=[display only the first x threads from the search results]:number of threads to show: ' \ '--first=[omit the first x threads from the search results]:number of threads to omit: ' \ -'--sort=[sort results]:sorting:((newest-first\:reverse chronological order oldest-first\:chronological order))' +'--sort=[sort results]:sorting:((newest-first\:reverse chronological order oldest-first\:chronological order))' \ +'--output=[select what to output]:output:((summary threads messages files tags sender recipients))' } _notmuch() diff --git a/doc/man1/notmuch-search.rst b/doc/man1/notmuch-search.rst index 90160f2..c9d38b1 100644 --- a/doc/man1/notmuch-search.rst +++ b/doc/man1/notmuch-search.rst @@ -35,7 +35,7 @@ Supported options for **search** include intended for programs that invoke **notmuch(1)** internally. If omitted, the latest supported version will be used. -``--output=(summary|threads|messages|files|tags)`` +``--output=(summary|threads|messages|files|tags|sender|recipients)`` **summary** Output a summary of each thread with any message matching @@ -78,6 +78,26 @@ Supported options for **search** include by null characters (--format=text0), as a JSON array (--format=json), or as an S-Expression list (--format=sexp). +**sender** +Output all addresses from the *From* header that appear on +any message matching the search terms, either one per line +(--format=text), separated by null characters +(--format=text0), as a JSON array (--format=json), or as +an S-Expression list (--format=sexp). + +Note: Searching for **sender** should be much faster than +searching for **recipients**, because sender addresses are +cached directly in the database whereas other addresses +need to be fetched from message files. + +**recipients** +Like **sender** but for addresses from *To*, *Cc* and +*Bcc* headers. + +This option can be given multiple times to combine different +outputs. Curently, this is only supported for **sender** and +**recipients** outputs. + ``--sort=``\ (**newest-first**\ \|\ **oldest-first**) This option can be used to present results in either chronological order (**oldest-first**) or reverse chronological diff --git a/notmuch-search.c b/notmuch-search.c index 5ac2a26..74588f8 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -23,11 +23,14 @@ #include string-util.h typedef enum { -OUTPUT_SUMMARY, -OUTPUT_THREADS, -OUTPUT_MESSAGES, -OUTPUT_FILES, -OUTPUT_TAGS +OUTPUT_SUMMARY = 1 0, +OUTPUT_THREADS = 1 1, +OUTPUT_MESSAGES = 1 2, +OUTPUT_FILES= 1 3, +OUTPUT_TAGS = 1 4, +OUTPUT_SENDER = 1 5, +OUTPUT_RECIPIENTS = 1 6, +OUTPUT_ADDRESSES= OUTPUT_SENDER | OUTPUT_RECIPIENTS, I think I would drop the OUTPUT_ADDRESSES enum as the parser no longer uses it (and replace the one use by OUTPUT_SENDER | OUTPUT_RECIPIENTS below). As mentioned elsewhere, this is required to suppress the following warning.
Re: [PATCH v1 1/3] search: Seperately report matching and non-matching authors.
Quoth Mark Walters on Oct 24 at 10:23 am: Hi I definitely like the idea: some comments below. Agreed. On Fri, 24 Oct 2014, David Edmondson d...@dme.org wrote: In addition to the :authors attribute of each search result, include :authors_matched and :authors_non_matched attributes. Both attributes are always included. If there are no non-matching authors, the :authors_non_matched attribute is set to the empty string. What about having both authors_matched and authors_not_matched as lists of authors (ie one string for each author)? Then emacs, for example, wouldn't try and parse the string back into authors before splitting. And authors_non_matched could be an empty list when appropriate which seems more natural than the empty string. All the above is based on what a client might want in the output rather than what is easy or sensible to implement in the C code. I was going to suggest exactly the same thing. And I think there's a fairly easy way to do it in C code that will also prevent library interface bloat: instead of introducing new library APIs to get at this information, just use the existing notmuch_thread_get_messages API and construct the matched and non-matched lists in the CLI. Doing it in the CLI wouldn't require the library to export yet another string list structure, which is always a huge pain (thanks C!), and wouldn't introduce more helper functions into the library API. I think the only disadvantage to doing this would be some duplication of the {matched_,}authors_{array,hash} logic into the CLI, but those are only there to support notmuch_thread_get_authors, which I think is a huge hack and should go away in the long term. (And, by doing this list building in the CLI, we avoid further embellishing the unnecessary get thread authors API). ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v3.1 3/9] lib: Introduce macros for bit operations
These macros help clarify basic bit-twiddling code and are written to be robust against C undefined behavior of shift operators. --- lib/message.cc| 6 +++--- lib/notmuch-private.h | 11 +++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/lib/message.cc b/lib/message.cc index 38bc929..55d2ff6 100644 --- a/lib/message.cc +++ b/lib/message.cc @@ -869,7 +869,7 @@ notmuch_bool_t notmuch_message_get_flag (notmuch_message_t *message, notmuch_message_flag_t flag) { -return message-flags (1 flag); +return NOTMUCH_TEST_BIT (message-flags, flag); } void @@ -877,9 +877,9 @@ notmuch_message_set_flag (notmuch_message_t *message, notmuch_message_flag_t flag, notmuch_bool_t enable) { if (enable) - message-flags |= (1 flag); + NOTMUCH_SET_BIT (message-flags, flag); else - message-flags = ~(1 flag); + NOTMUCH_CLEAR_BIT (message-flags, flag); } time_t diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h index 36cc12b..b86897c 100644 --- a/lib/notmuch-private.h +++ b/lib/notmuch-private.h @@ -63,6 +63,17 @@ NOTMUCH_BEGIN_DECLS #define STRNCMP_LITERAL(var, literal) \ strncmp ((var), (literal), sizeof (literal) - 1) +/* Robust bit test/set/reset macros */ +#define NOTMUCH_TEST_BIT(val, bit) \ +(((bit) 0 || (bit) = CHAR_BIT * sizeof (unsigned long long)) ? 0 \ + : !!((val) (1ull (bit +#define NOTMUCH_SET_BIT(valp, bit) \ +(((bit) 0 || (bit) = CHAR_BIT * sizeof (unsigned long long)) ? *(valp) \ + : (*(valp) |= (1ull (bit +#define NOTMUCH_CLEAR_BIT(valp, bit) \ +(((bit) 0 || (bit) = CHAR_BIT * sizeof (unsigned long long)) ? *(valp) \ + : (*(valp) = ~(1ull (bit + #define unused(x) x __attribute__ ((unused)) #ifdef __cplusplus -- 2.1.0 ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
how to put into a journal info about the email sent
Dear All, i'm using org. And I'm using notmuch (that's why I address both mailing lists). Now, writing an email in everyday bussiness requires a non-significant time of your workhours. So I'd like to have this event in my org agenda. So any time I send some email with a given subject, I'd like to 'automatically' entry the information about it into e.g. sentmails.org in form of a diary entry, with appropriate tag. In example: I'm sending a mail to Tom, with subject 'dealing vme register mapping'. At the moment I send this email (using smtpmail), I'd like an entry in the sentmails.org as follows: ** 2014-10 October *** 2014-10-02 Thursday dealing vme register mapping :mail: [[notmuch:id:7a97bb93e66a41878edd4c04fa764...@cernfe03.cern.ch][dealing vme register mapping]] I thought that I can use following (add-hook 'message-send-hook 'mysuperfunction) to hook on sending and generate a capture entry. In fact this works pretty well _EXCEPT_ the link to the mail sent. The org-store-link cannot apparently store a link to an email, which so far was not sent (and not received?) because it claims that 'org-gnus-store-link: Can not create link: No Gcc header found' Hence this is pretty fatal for my diary entry. My question is: is there any way how to link not-yet-sent/received email as an org-link? Or is there any way to generate Gcc header before the email is sent and use this header during sending? Or is there any other way how to put into my agenda sent emails? many thanks david ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
Re: [O] how to put into a journal info about the email sent
On Fri, Oct 24 2014, Eric Abrahamsen wrote: David Belohrad da...@belohrad.ch writes: Dear All, i'm using org. And I'm using notmuch (that's why I address both mailing lists). Now, writing an email in everyday bussiness requires a non-significant time of your workhours. So I'd like to have this event in my org agenda. So any time I send some email with a given subject, I'd like to 'automatically' entry the information about it into e.g. sentmails.org in form of a diary entry, with appropriate tag. I do something like this in Gnorb, which I'd recommend you use except it's mostly Gnus specific. I do it in two parts, but you could do it in one. Basically I add a function to the `message-header-hook' (which ensures that all the message headers have been generated properly). Does `message-generate-headers-first' not do what you want for this specific part? Obviously the downside is that, without a Gcc: header, org can't actually make a real link to the message. It doesn't know where it's going to be. However if you know that all your sent messages can be reached with a link that looks like notmuch:id#Message-id, then you can make that yourself in your org capture template with something like As you suggest, know the message-id should be good enough to generate a notmuch link, though you may have to wait for the notmuch index to be updated for the link to be valid. ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
Re: Looking for the perfect mail client
Sepp Tannhuber wrote: Ian Main im...@redhat.com schrieb am 6:28 Freitag, 24.Oktober 2014: I wonder if you'd be willing to give the vim client a go. We've done a lot of work to it lately and I think it would handle all you listed here. For me, it's hard to get familiar with it because I have not found a detailed documentation. The README file is not good enough for a stupid person. Interesting, good feedback. Is it the usage that is difficult or setup? I also doubt that it can render html properly. Well like any other text based client it uses a text dump of a links or such (I forget which one). We have great support for web browser viewing of the email though if that isn't good enough. Nevertheless, I would give it a try if I could find useful documentation. Let me know if you have suggestions on where it's lacking. We need a better installation guide plus a tutorial maybe? Ian ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v3] VIM: Add URI handling
This patch adds URI handling to the vim client. You can now press 'enter' by default and the client will parse the current line and find any 'Part's or URIs available for opening. If there are more than one it opens the one under the cursor or else it opens the only one available. It also supports mailto: URI's and will compose a new message when activated. By default xdg-open is used for everything but mailto: which generally does the right thing afaict. Note that this is now dependant on the attachment patch in order to make the nice 'enter' behavior work for both. Ian --- Fix commit message formatting. vim/notmuch.txt | 3 ++- vim/notmuch.vim | 76 +++-- 2 files changed, 70 insertions(+), 9 deletions(-) diff --git a/vim/notmuch.txt b/vim/notmuch.txt index 838a904..5d84fde 100644 --- a/vim/notmuch.txt +++ b/vim/notmuch.txt @@ -74,7 +74,8 @@ I Mark as read (-unread) t Tag (prompted) e Extract attachment on the current 'Attachment' line or all attachments if the cursor is elsewhere. -v View attachment on the current 'Attachment' line. +enter View email part on the current 'Part' line, or open URI under cursor +or on line. s Search p Save patches r Reply diff --git a/vim/notmuch.vim b/vim/notmuch.vim index 1466e50..2f76f55 100644 --- a/vim/notmuch.vim +++ b/vim/notmuch.vim @@ -12,7 +12,7 @@ let g:notmuch_folders_maps = { \ 'Enter':'folders_show_search()', \ 's': 'folders_search_prompt()', \ '=': 'folders_refresh()', - \ 'c': 'compose()', + \ 'c': 'compose()', \ } let g:notmuch_search_maps = { @@ -25,7 +25,7 @@ let g:notmuch_search_maps = { \ 's': 'search_search_prompt()', \ '=': 'search_refresh()', \ '?': 'search_info()', - \ 'c': 'compose()', + \ 'c': 'compose()', \ } let g:notmuch_show_maps = { @@ -35,13 +35,13 @@ let g:notmuch_show_maps = { \ 't': 'show_tag()', \ 'o': 'show_open_msg()', \ 'e': 'show_extract_msg()', - \ 'Enter':'show_view_attachment()', + \ 'Enter':'show_view_magic()', \ 's': 'show_save_msg()', \ 'p': 'show_save_patches()', \ 'r': 'show_reply()', \ '?': 'show_info()', \ 'Tab': 'show_next_msg()', - \ 'c': 'compose()', + \ 'c': 'compose()', \ } let g:notmuch_compose_maps = { @@ -63,6 +63,7 @@ let s:notmuch_view_attachment_default = 'xdg-open' let s:notmuch_attachment_tmpdir_default = '~/.notmuch/tmp' let s:notmuch_folders_count_threads_default = 0 let s:notmuch_compose_start_insert_default = 1 +let s:notmuch_open_uri_default = 'xdg-open' function! s:new_file_buffer(type, fname) exec printf('edit %s', a:fname) @@ -141,8 +142,8 @@ function! s:show_reply() end endfunction -function! s:compose() - ruby open_compose +function! s:compose(to_email) + ruby open_compose(VIM::evaluate('a:to_email')) let b:compose_done = 0 call s:set_map(g:notmuch_compose_maps) autocmd BufDelete buffer call s:on_compose_delete() @@ -155,6 +156,22 @@ function! s:show_info() ruby vim_puts get_message.inspect endfunction +function! s:show_view_magic() + let line = getline(.) + +ruby EOF + line = VIM::evaluate('line') + + # Easiest to check for 'Part' types first.. + match = line.match(/^Part (\d*):/) + if match and match.length == 2 + VIM::command('call s:show_view_attachment()') + else + VIM::command('call s:show_open_uri()') + end +EOF +endfunction + function! s:show_view_attachment() let line = getline(.) ruby EOF @@ -226,6 +243,45 @@ ruby EOF EOF endfunction +function! s:show_open_uri() + let line = getline(.) + let pos = getpos(.) + let col = pos[2] +ruby EOF + m = get_message + line = VIM::evaluate('line') + col = VIM::evaluate('col') - 1 + uris = URI.extract(line) + wanted_uri = nil + if uris.length == 1 + wanted_uri = uris[0] + else + uris.each do |uri| + # Check to see the URI is at the present cursor location + idx = line.index(uri) + if col = idx and col = idx + uri.length + wanted_uri = uri + break + end + end + end + + if wanted_uri + uri = URI.parse(wanted_uri) + if uri.class == URI::MailTo + vim_puts(Composing new email to #{uri.to}.) + VIM::command(call s:compose('#{uri.to}')) + else +
Re: [PATCH v3] VIM: Add URI handling
On Fri, Oct 24 2014, Ian Main im...@stemwinder.org wrote: This patch does not apply on top of notmuchmail master (commit 38240d106139da8). This patch adds URI handling to the vim client. You can now press Although insignificant, I'll start commenting on all sent patches (that I look into) which talk like 'This patch adds' -- sure it is patch when email is sent, but in repository it is not so -- so the commit message should not mention it (but I do not require changing it). Anyway, I suspect this change will become applicable after some other change is committed first -- and IMO this could stay as non-stale patch provided that SomeOne(TM) informs what is the message that contains changes (or series those) that is required for this message to apply. BTW: does the content of this change differ much from v1 at id:1412281423-22441-1-git-send-email-im...@stemwinder.org or should I re-check (carefully!) that the changes are still OK. Tomi 'enter' by default and the client will parse the current line and find any 'Part's or URIs available for opening. If there are more than one it opens the one under the cursor or else it opens the only one available. It also supports mailto: URI's and will compose a new message when activated. By default xdg-open is used for everything but mailto: which generally does the right thing afaict. Note that this is now dependant on the attachment patch in order to make the nice 'enter' behavior work for both. Ian --- Fix commit message formatting. vim/notmuch.txt | 3 ++- vim/notmuch.vim | 76 +++-- 2 files changed, 70 insertions(+), 9 deletions(-) diff --git a/vim/notmuch.txt b/vim/notmuch.txt index 838a904..5d84fde 100644 --- a/vim/notmuch.txt +++ b/vim/notmuch.txt @@ -74,7 +74,8 @@ I Mark as read (-unread) tTag (prompted) e Extract attachment on the current 'Attachment' line or all attachments if the cursor is elsewhere. -v View attachment on the current 'Attachment' line. +enter View email part on the current 'Part' line, or open URI under cursor +or on line. sSearch pSave patches rReply diff --git a/vim/notmuch.vim b/vim/notmuch.vim index 1466e50..2f76f55 100644 --- a/vim/notmuch.vim +++ b/vim/notmuch.vim @@ -12,7 +12,7 @@ let g:notmuch_folders_maps = { \ 'Enter':'folders_show_search()', \ 's': 'folders_search_prompt()', \ '=': 'folders_refresh()', - \ 'c': 'compose()', + \ 'c': 'compose()', \ } let g:notmuch_search_maps = { @@ -25,7 +25,7 @@ let g:notmuch_search_maps = { \ 's': 'search_search_prompt()', \ '=': 'search_refresh()', \ '?': 'search_info()', - \ 'c': 'compose()', + \ 'c': 'compose()', \ } let g:notmuch_show_maps = { @@ -35,13 +35,13 @@ let g:notmuch_show_maps = { \ 't': 'show_tag()', \ 'o': 'show_open_msg()', \ 'e': 'show_extract_msg()', - \ 'Enter':'show_view_attachment()', + \ 'Enter':'show_view_magic()', \ 's': 'show_save_msg()', \ 'p': 'show_save_patches()', \ 'r': 'show_reply()', \ '?': 'show_info()', \ 'Tab': 'show_next_msg()', - \ 'c': 'compose()', + \ 'c': 'compose()', \ } let g:notmuch_compose_maps = { @@ -63,6 +63,7 @@ let s:notmuch_view_attachment_default = 'xdg-open' let s:notmuch_attachment_tmpdir_default = '~/.notmuch/tmp' let s:notmuch_folders_count_threads_default = 0 let s:notmuch_compose_start_insert_default = 1 +let s:notmuch_open_uri_default = 'xdg-open' function! s:new_file_buffer(type, fname) exec printf('edit %s', a:fname) @@ -141,8 +142,8 @@ function! s:show_reply() end endfunction -function! s:compose() - ruby open_compose +function! s:compose(to_email) + ruby open_compose(VIM::evaluate('a:to_email')) let b:compose_done = 0 call s:set_map(g:notmuch_compose_maps) autocmd BufDelete buffer call s:on_compose_delete() @@ -155,6 +156,22 @@ function! s:show_info() ruby vim_puts get_message.inspect endfunction +function! s:show_view_magic() + let line = getline(.) + +ruby EOF + line = VIM::evaluate('line') + + # Easiest to check for 'Part' types first.. + match = line.match(/^Part (\d*):/) + if match and match.length == 2 + VIM::command('call s:show_view_attachment()') + else + VIM::command('call s:show_open_uri()') + end +EOF +endfunction + function! s:show_view_attachment() let line = getline(.) ruby EOF @@ -226,6 +243,45 @@ ruby EOF EOF endfunction +function! s:show_open_uri() + let line = getline(.) + let pos = getpos(.) + let col = pos[2] +ruby EOF +
[PATCH v4 2/2] VIM: Add URI handling
Add URI handling to the vim client. You can now press 'enter' by default and the client will parse the current line and find any 'Part's or URIs available for opening. If there are more than one it opens the one under the cursor or else it opens the only one available. It also supports mailto: URI's and will compose a new message when activated. By default xdg-open is used for everything but mailto: which generally does the right thing afaict. Note that this is now dependant on the attachment patch in order to make the nice 'enter' behavior work for both. Ian --- Fixed commit message. vim/notmuch.txt | 3 ++- vim/notmuch.vim | 76 +++-- 2 files changed, 70 insertions(+), 9 deletions(-) diff --git a/vim/notmuch.txt b/vim/notmuch.txt index d5e1ad2..f51b20f 100644 --- a/vim/notmuch.txt +++ b/vim/notmuch.txt @@ -74,7 +74,8 @@ I Mark as read (-unread) t Tag (prompted) e Extract attachment on the current 'Part' line or all attachments if the cursor is elsewhere. -enter View attachment on the current 'Part' line. +enter View email part on the current 'Part' line, or open URI under cursor +or on line. s Search p Save patches r Reply diff --git a/vim/notmuch.vim b/vim/notmuch.vim index 1466e50..2f76f55 100644 --- a/vim/notmuch.vim +++ b/vim/notmuch.vim @@ -12,7 +12,7 @@ let g:notmuch_folders_maps = { \ 'Enter':'folders_show_search()', \ 's': 'folders_search_prompt()', \ '=': 'folders_refresh()', - \ 'c': 'compose()', + \ 'c': 'compose()', \ } let g:notmuch_search_maps = { @@ -25,7 +25,7 @@ let g:notmuch_search_maps = { \ 's': 'search_search_prompt()', \ '=': 'search_refresh()', \ '?': 'search_info()', - \ 'c': 'compose()', + \ 'c': 'compose()', \ } let g:notmuch_show_maps = { @@ -35,13 +35,13 @@ let g:notmuch_show_maps = { \ 't': 'show_tag()', \ 'o': 'show_open_msg()', \ 'e': 'show_extract_msg()', - \ 'Enter':'show_view_attachment()', + \ 'Enter':'show_view_magic()', \ 's': 'show_save_msg()', \ 'p': 'show_save_patches()', \ 'r': 'show_reply()', \ '?': 'show_info()', \ 'Tab': 'show_next_msg()', - \ 'c': 'compose()', + \ 'c': 'compose()', \ } let g:notmuch_compose_maps = { @@ -63,6 +63,7 @@ let s:notmuch_view_attachment_default = 'xdg-open' let s:notmuch_attachment_tmpdir_default = '~/.notmuch/tmp' let s:notmuch_folders_count_threads_default = 0 let s:notmuch_compose_start_insert_default = 1 +let s:notmuch_open_uri_default = 'xdg-open' function! s:new_file_buffer(type, fname) exec printf('edit %s', a:fname) @@ -141,8 +142,8 @@ function! s:show_reply() end endfunction -function! s:compose() - ruby open_compose +function! s:compose(to_email) + ruby open_compose(VIM::evaluate('a:to_email')) let b:compose_done = 0 call s:set_map(g:notmuch_compose_maps) autocmd BufDelete buffer call s:on_compose_delete() @@ -155,6 +156,22 @@ function! s:show_info() ruby vim_puts get_message.inspect endfunction +function! s:show_view_magic() + let line = getline(.) + +ruby EOF + line = VIM::evaluate('line') + + # Easiest to check for 'Part' types first.. + match = line.match(/^Part (\d*):/) + if match and match.length == 2 + VIM::command('call s:show_view_attachment()') + else + VIM::command('call s:show_open_uri()') + end +EOF +endfunction + function! s:show_view_attachment() let line = getline(.) ruby EOF @@ -226,6 +243,45 @@ ruby EOF EOF endfunction +function! s:show_open_uri() + let line = getline(.) + let pos = getpos(.) + let col = pos[2] +ruby EOF + m = get_message + line = VIM::evaluate('line') + col = VIM::evaluate('col') - 1 + uris = URI.extract(line) + wanted_uri = nil + if uris.length == 1 + wanted_uri = uris[0] + else + uris.each do |uri| + # Check to see the URI is at the present cursor location + idx = line.index(uri) + if col = idx and col = idx + uri.length + wanted_uri = uri + break + end + end + end + + if wanted_uri + uri = URI.parse(wanted_uri) + if uri.class == URI::MailTo + vim_puts(Composing new email to #{uri.to}.) + VIM::command(call s:compose('#{uri.to}')) + else + vim_puts(Opening #{uri.to_s}.) +
[PATCH v4 1/2] VIM: Add better attachment support
Change how the notmuch vim client supports attachments: - For each message part a 'Part number: filename' is added to the header. - You can then use 'e' to extract the attachment under the cursor or use it elsewhere to extract all attachments (the prior behavior) - You can use 'v' to 'view' the attachment/part using xdg-open by default. - If the message is 'text/html' we include a 'Part:' for the body of the message so you can easily view it in a web browser if you so choose. Ian --- - Fixed commit message - Fixed documentation vim/notmuch.txt | 8 +- vim/notmuch.vim | 84 +++-- 2 files changed, 89 insertions(+), 3 deletions(-) diff --git a/vim/notmuch.txt b/vim/notmuch.txt index 4374102..d5e1ad2 100644 --- a/vim/notmuch.txt +++ b/vim/notmuch.txt @@ -72,6 +72,9 @@ q Quit view A Archive (-inbox -unread) I Mark as read (-unread) t Tag (prompted) +e Extract attachment on the current 'Part' line or all + attachments if the cursor is elsewhere. +enter View attachment on the current 'Part' line. s Search p Save patches r Reply @@ -148,6 +151,9 @@ You can also configure your externail mail reader and sendemail program: let g:notmuch_reader = 'mutt -f %s' let g:notmuch_sendmail = 'sendmail' - + +You can also configure what probram is used to view attachments: + + let g:notmuch_view_attachment = 'xdg-open' vim:tw=78:ts=8:noet:ft=help: diff --git a/vim/notmuch.vim b/vim/notmuch.vim index cad9517..1466e50 100644 --- a/vim/notmuch.vim +++ b/vim/notmuch.vim @@ -35,6 +35,7 @@ let g:notmuch_show_maps = { \ 't': 'show_tag()', \ 'o': 'show_open_msg()', \ 'e': 'show_extract_msg()', + \ 'Enter':'show_view_attachment()', \ 's': 'show_save_msg()', \ 'p': 'show_save_patches()', \ 'r': 'show_reply()', @@ -58,6 +59,8 @@ let s:notmuch_date_format_default = '%d.%m.%y' let s:notmuch_datetime_format_default = '%d.%m.%y %H:%M:%S' let s:notmuch_reader_default = 'mutt -f %s' let s:notmuch_sendmail_default = 'sendmail' +let s:notmuch_view_attachment_default = 'xdg-open' +let s:notmuch_attachment_tmpdir_default = '~/.notmuch/tmp' let s:notmuch_folders_count_threads_default = 0 let s:notmuch_compose_start_insert_default = 1 @@ -152,13 +155,72 @@ function! s:show_info() ruby vim_puts get_message.inspect endfunction +function! s:show_view_attachment() + let line = getline(.) +ruby EOF + m = get_message + line = VIM::evaluate('line') + + match = line.match(/^Part (\d*):/) + if match and match.length == 2 + # Set up the tmpdir + tmpdir = VIM::evaluate('g:notmuch_attachment_tmpdir') + tmpdir = File.expand_path(tmpdir) + Dir.mkdir(tmpdir) unless Dir.exists?(tmpdir) + + p = m.mail.parts[match[1].to_i - 1] + if p == nil + # Not a multipart message, use the message itself. + p = m.mail + end + if p.filename and p.filename.length 0 + filename = p.filename + else + suffix = '' + if p.mime_type == 'text/html' + suffix = '.html' + end + filename = part-#{match[1]}#{suffix} + end + + # Sanitize just in case.. + filename.gsub!(/[^0-9A-Za-z.\-]/, '_') + + fullpath = File.expand_path(#{tmpdir}/#{filename}) + vim_puts Viewing attachment #{fullpath} + File.open(fullpath, 'w') do |f| + f.write p.body.decoded + cmd = VIM::evaluate('g:notmuch_view_attachment') + system(cmd, fullpath) + end + else + vim_puts No attachment on this line. + end +EOF +endfunction + function! s:show_extract_msg() + let line = getline(.) ruby EOF m = get_message - m.mail.attachments.each do |a| + line = VIM::evaluate('line') + + # If the user is on a line that has an 'Part' + # line, we just extract the one attachment. + match = line.match(/^Part (\d*):/) + if match and match.length == 2 + a = m.mail.parts[match[1].to_i - 1] File.open(a.filename, 'w') do |f| f.write a.body.decoded - print Extracted '#{a.filename}' + vim_puts Extracted #{a.filename} + end + else + # Extract them all.. + m.mail.attachments.each do |a| + File.open(a.filename, 'w') do |f| + f.write a.body.decoded + vim_puts Extracted
Re: [O] how to put into a journal info about the email sent
David Edmondson d...@dme.org writes: On Fri, Oct 24 2014, Eric Abrahamsen wrote: David Belohrad da...@belohrad.ch writes: Dear All, i'm using org. And I'm using notmuch (that's why I address both mailing lists). Now, writing an email in everyday bussiness requires a non-significant time of your workhours. So I'd like to have this event in my org agenda. So any time I send some email with a given subject, I'd like to 'automatically' entry the information about it into e.g. sentmails.org in form of a diary entry, with appropriate tag. I do something like this in Gnorb, which I'd recommend you use except it's mostly Gnus specific. I do it in two parts, but you could do it in one. Basically I add a function to the `message-header-hook' (which ensures that all the message headers have been generated properly). Does `message-generate-headers-first' not do what you want for this specific part? Yeah, I think I looked at that previously. But this thing is going in a hook anyway, might as well use the hook that *doesn't* require me to call that function explicitly. Obviously the downside is that, without a Gcc: header, org can't actually make a real link to the message. It doesn't know where it's going to be. However if you know that all your sent messages can be reached with a link that looks like notmuch:id#Message-id, then you can make that yourself in your org capture template with something like As you suggest, know the message-id should be good enough to generate a notmuch link, though you may have to wait for the notmuch index to be updated for the link to be valid. Yup, I've got the same issue with nnimap -- you have to wait for the next sync to get access to the message. So far it hasn't been a problem, though. ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v2 1/3] search: Separately report matching and non-matching authors.
In addition to the 'authors' attribute of each search result, include 'authors_matched' and 'authors_non_matched' attributes. Both attributes are always included and are formatted as a list of authors. If there are no matching authors, the 'authors_non_matched' attribute is set to the empty list. --- notmuch-search.c | 105 +++ 1 file changed, 105 insertions(+) diff --git a/notmuch-search.c b/notmuch-search.c index bc9be45..18c3b20 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -22,6 +22,8 @@ #include sprinter.h #include string-util.h +#include glib.h + typedef enum { OUTPUT_SUMMARY, OUTPUT_THREADS, @@ -69,6 +71,105 @@ get_thread_query (notmuch_thread_t *thread, return 0; } +/* Return a more pleasent rendering of the mail address + * `nasty_author'. */ +static const char * +_nice_author (void *ctx, const char *nasty_author) +{ +const char *nice_author = NULL; + +InternetAddressList *list = internet_address_list_parse_string (nasty_author); +if (list) { + InternetAddress *address = internet_address_list_get_address (list, 0); + if (address) { + nice_author = internet_address_get_name (address); + if (nice_author == NULL) { + InternetAddressMailbox *mailbox = INTERNET_ADDRESS_MAILBOX (address); + nice_author = internet_address_mailbox_get_addr (mailbox); + } + } + /* Duplicate the string before `g_object_unref' destroys +* it. */ + if (nice_author) + nice_author = talloc_strdup (ctx, nice_author); + + g_object_unref (G_OBJECT (list)); +} + +if (nice_author) + return nice_author; +else + return nasty_author; +} + +static int +_enumerate_authors (sprinter_t *format, +notmuch_thread_t *thread) +{ +notmuch_messages_t *messages; +GHashTable *matched_hash = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); +GHashTable *unmatched_hash = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); +GPtrArray *matched_array = g_ptr_array_new (); +GPtrArray *unmatched_array = g_ptr_array_new (); + +/* Iterate over the messages in the thread collecting matching and + * non-matching authors. */ +for (messages = notmuch_thread_get_messages (thread); +notmuch_messages_valid (messages); +notmuch_messages_move_to_next (messages)) +{ + notmuch_message_t *message = notmuch_messages_get (messages); + const char *author = _nice_author (thread, notmuch_message_get_header (message, from)); + + if (author) { + GHashTable *hash; + GPtrArray *array; + + if (notmuch_message_get_flag (message, NOTMUCH_MESSAGE_FLAG_MATCH)) { + hash = matched_hash; + array = matched_array; + } else { + hash = unmatched_hash; + array = unmatched_array; + } + + if (!g_hash_table_lookup_extended (hash, author, NULL, NULL)) { + char *copy = talloc_strdup (thread, author); + g_hash_table_insert (hash, copy, NULL); + g_ptr_array_add (array, (char *) copy); + } + } +} + +/* Output the matched authors. */ +unsigned int i; +format-map_key (format, authors_matched); +format-begin_list (format); +for (i = 0; i matched_array-len; i++) + format-string (format, (char *) g_ptr_array_index( matched_array, i)); +format-end (format); + +/* Output the non-matched authors, but not if they were seen + * already in the matched authors list. */ +format-map_key (format, authors_non_matched); +format-begin_list (format); +for (i = 0; i unmatched_array-len; i++) { + char *author = (char *) g_ptr_array_index( unmatched_array, i); + + if (!g_hash_table_lookup_extended (matched_hash, author, NULL, NULL)) + format-string (format, author); +} +format-end (format); + +g_hash_table_unref (matched_hash); +g_hash_table_unref (unmatched_hash); + +g_ptr_array_free (matched_array, TRUE); +g_ptr_array_free (unmatched_array, TRUE); + +return 0; +} + static int do_search_threads (sprinter_t *format, notmuch_query_t *query, @@ -152,6 +253,10 @@ do_search_threads (sprinter_t *format, format-integer (format, total); format-map_key (format, authors); format-string (format, authors); + if (_enumerate_authors (format, thread) 0) { + fprintf (stderr, Out of memory\n); + return 1; + } format-map_key (format, subject); format-string (format, subject); if (notmuch_format_version = 2) { -- 2.1.1 ___ notmuch mailing list notmuch@notmuchmail.org
[PATCH v2 0/3] Improve the display of matching/non-matching authors.
Improve the display of matching/non-matching authors. Distinguishing between matching and non-matching authors in the emacs interface is currently done by parsing the :authors attribute of a search result. If one of the authors uses the pipe symbol (|) in their 'From' address this parsing incorrectly determines the matching and non-matching authors. Address this by adding explicit matching and non-matching authors attributes to the structured output formats. v2: - Return the matching/non-matching authors as a list. - More improvements to the code that renders the authors are possible (to improve the chosen break between visible and invisible), but a planned re-write of the `notmuch-search-result-format' code would render that irrelevant. David Edmondson (3): search: Separately report matching and non-matching authors. emacs: Improved display of matching/non-matching authors. test: Update tests for 'authors_matched' and authors_non_matched'. emacs/notmuch.el | 64 ++ notmuch-search.c | 105 +++ test/T160-json.sh| 9 test/T170-sexp.sh| 4 +- test/T470-missing-headers.sh | 8 5 files changed, 159 insertions(+), 31 deletions(-) -- 2.1.1 ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v2 3/3] test: Update tests for 'authors_matched' and authors_non_matched'.
--- test/T160-json.sh| 9 + test/T170-sexp.sh| 4 ++-- test/T470-missing-headers.sh | 8 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/test/T160-json.sh b/test/T160-json.sh index c1cf649..0a8df18 100755 --- a/test/T160-json.sh +++ b/test/T160-json.sh @@ -25,6 +25,10 @@ test_expect_equal_json $output [{\thread\: \XXX\, \matched\: 1, \total\: 1, \authors\: \Notmuch Test Suite\, + \authors_matched\: [ + \Notmuch Test Suite\ + ], + \authors_non_matched\: [], \subject\: \json-search-subject\, \query\: [\id:$gen_msg_id\, null], \tags\: [\inbox\, @@ -59,6 +63,11 @@ test_expect_equal_json $output [{\thread\: \XXX\, \matched\: 1, \total\: 1, \authors\: \Notmuch Test Suite\, + \authors\: \Notmuch Test Suite\, + \authors_matched\: [ + \Notmuch Test Suite\ + ], + \authors_non_matched\: [], \subject\: \json-search-utf8-body-sübjéct\, \query\: [\id:$gen_msg_id\, null], \tags\: [\inbox\, diff --git a/test/T170-sexp.sh b/test/T170-sexp.sh index 667e319..f2a08bf 100755 --- a/test/T170-sexp.sh +++ b/test/T170-sexp.sh @@ -19,7 +19,7 @@ test_expect_equal $output :id \${gen_msg_id}\ :match t :excluded nil :f test_begin_subtest Search message: sexp add_message [subject]=\sexp-search-subject\ [date]=\Sat, 01 Jan 2000 12:00:00 -\ [body]=\sexp-search-message\ output=$(notmuch search --format=sexp sexp-search-message | notmuch_search_sanitize) -test_expect_equal $output ((:thread \0002\ :timestamp 946728000 :date_relative \2000-01-01\ :matched 1 :total 1 :authors \Notmuch Test Suite\ :subject \sexp-search-subject\ :query (\id:$gen_msg_id\ nil) :tags (\inbox\ \unread\))) +test_expect_equal $output ((:thread \0002\ :timestamp 946728000 :date_relative \2000-01-01\ :matched 1 :total 1 :authors \Notmuch Test Suite\ :authors_matched (\Notmuch Test Suite\) :authors_non_matched () :subject \sexp-search-subject\ :query (\id:$gen_msg_id\ nil) :tags (\inbox\ \unread\))) test_begin_subtest Show message: sexp, utf-8 add_message [subject]=\sexp-show-utf8-body-sübjéct\ [date]=\Sat, 01 Jan 2000 12:00:00 -\ [body]=\jsön-show-méssage\ @@ -44,7 +44,7 @@ test_expect_equal $output :id \$id\ :match t :excluded nil :filename \ test_begin_subtest Search message: sexp, utf-8 add_message [subject]=\sexp-search-utf8-body-sübjéct\ [date]=\Sat, 01 Jan 2000 12:00:00 -\ [body]=\jsön-search-méssage\ output=$(notmuch search --format=sexp jsön-search-méssage | notmuch_search_sanitize) -test_expect_equal $output ((:thread \0005\ :timestamp 946728000 :date_relative \2000-01-01\ :matched 1 :total 1 :authors \Notmuch Test Suite\ :subject \sexp-search-utf8-body-sübjéct\ :query (\id:$gen_msg_id\ nil) :tags (\inbox\ \unread\))) +test_expect_equal $output ((:thread \0005\ :timestamp 946728000 :date_relative \2000-01-01\ :matched 1 :total 1 :authors \Notmuch Test Suite\ :authors_matched (\Notmuch Test Suite\) :authors_non_matched () :subject \sexp-search-utf8-body-sübjéct\ :query (\id:$gen_msg_id\ nil) :tags (\inbox\ \unread\))) test_done diff --git a/test/T470-missing-headers.sh b/test/T470-missing-headers.sh index cb38301..250a370 100755 --- a/test/T470-missing-headers.sh +++ b/test/T470-missing-headers.sh @@ -34,6 +34,10 @@ test_expect_equal_json $output ' [ { authors: , +authors_matched: [ + +], +authors_non_matched: [], date_relative: 2001-01-05, matched: 1, subject: , @@ -48,6 +52,10 @@ test_expect_equal_json $output ' }, { authors: Notmuch Test Suite, +authors_matched: [ +Notmuch Test Suite +], +authors_non_matched: [], date_relative: 1970-01-01, matched: 1, subject: , -- 2.1.1 ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v2 2/3] emacs: Improved display of matching/non-matching authors.
Rather than splitting the :authors attribute, which is error prone, use the separate :authors_matched and :authors_non_matched attributes. This improves the display of authors should one of them include a pipe symbol (|) in their 'from' address. --- emacs/notmuch.el | 64 +++- 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/emacs/notmuch.el b/emacs/notmuch.el index b44a907..688b37c 100644 --- a/emacs/notmuch.el +++ b/emacs/notmuch.el @@ -672,22 +672,24 @@ foreground and blue background. ;; Reverse the list so earlier entries take precedence (reverse notmuch-search-line-faces))) -(defun notmuch-search-author-propertize (authors) +(defun notmuch-search-author-propertize (authors matching-length) Split `authors' into matching and non-matching authors and propertize appropriately. If no boundary between authors and non-authors is found, assume that all of the authors match. - (if (string-match \\(.*\\)|\\(.*\\) authors) - (concat (propertize (concat (match-string 1 authors) ,) - 'face 'notmuch-search-matching-authors) - (propertize (match-string 2 authors) - 'face 'notmuch-search-non-matching-authors)) -(propertize authors 'face 'notmuch-search-matching-authors))) - -(defun notmuch-search-insert-authors (format-string authors) + (let ((match-part (substring authors 0 matching-length)) + (non-match-part (substring authors matching-length))) + + (concat (propertize match-part 'face 'notmuch-search-matching-authors) + (propertize non-match-part 'face 'notmuch-search-non-matching-authors + +(defun notmuch-search-insert-authors (format-string matching-authors non-matching-authors) ;; Save the match data to avoid interfering with ;; `notmuch-search-process-filter'. (save-match-data -(let* ((formatted-authors (format format-string authors)) +(let* ((authors (if (string= non-matching-authors) + matching-authors + (concat matching-authors , non-matching-authors))) + (formatted-authors (format format-string authors)) (formatted-sample (format format-string )) (visible-string formatted-authors) (invisible-string ) @@ -703,9 +705,9 @@ non-authors is found, assume that all of the authors match. (setq visible-string (substring formatted-authors 0 visible-length) invisible-string (substring formatted-authors visible-length)) ;; If possible, truncate the visible string at a natural - ;; break (comma or pipe), as incremental search doesn't - ;; match across the visible/invisible border. - (when (string-match \\(.*\\)\\([,|] \\)\\([^,|]*\\) visible-string) + ;; break (comma), as incremental search doesn't match + ;; across the visible/invisible border. + (when (string-match \\(.*\\)\\(, \\)\\([^,]*\\) visible-string) ;; Second clause is destructive on `visible-string', so ;; order is important. (setq invisible-string (concat (match-string 3 visible-string) @@ -721,20 +723,23 @@ non-authors is found, assume that all of the authors match. ? ;; Use different faces to show matching and non-matching authors. - (if (string-match \\(.*\\)|\\(.*\\) visible-string) - ;; The visible string contains both matching and - ;; non-matching authors. - (setq visible-string (notmuch-search-author-propertize visible-string) - ;; The invisible string must contain only non-matching - ;; authors, as the visible-string contains both. - invisible-string (propertize invisible-string -'face 'notmuch-search-non-matching-authors)) - ;; The visible string contains only matching authors. - (setq visible-string (propertize visible-string -'face 'notmuch-search-matching-authors) - ;; The invisible string may contain both matching and - ;; non-matching authors. - invisible-string (notmuch-search-author-propertize invisible-string))) + (let ((visible-length (length visible-string)) + (matching-length (length matching-authors))) + + (if ( visible-length matching-length) + ;; The visible string contains both matching and + ;; non-matching authors. + (setq visible-string (notmuch-search-author-propertize visible-string matching-length) + ;; The invisible string must contain only non-matching + ;; authors, as the visible-string contains both. + invisible-string (propertize invisible-string + 'face 'notmuch-search-non-matching-authors))
Re: Looking for the perfect mail client
Ian Main im...@stemwinder.org schrieb am 18:02 Freitag, 24.Oktober 2014: Interesting, good feedback. Is it the usage that is difficult or setup? Installation was not a problem for me although the files »notmuch.vim« and »notmuch.txt« were not found by »make install« for some reason. So I linked them into the notmuch-vim directory. After that »make install« succeeded. But the main obstacle is that new users must find by trial and error that they must type »s« to search, »t« to tag files and so on. It would be very useful to list all the key bindings and configuration options. I still don't know how to compose a new message. Well like any other text based client it uses a text dump of a links or such (I forget which one). I think »elinks --dump« We have great support for web browser viewing of the email though if that isn't good enough. Yes, it's 2014. ;-) Let me know if you have suggestions on where it's lacking. Okay, thank you so far. ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
Re: Looking for the perfect mail client
Sepp Tannhuber wrote: Ian Main im...@stemwinder.org schrieb am 18:02 Freitag, 24.Oktober 2014: Interesting, good feedback. Is it the usage that is difficult or setup? Installation was not a problem for me although the files »notmuch.vim« and »notmuch.txt« were not found by »make install« for some reason. So I linked them into the notmuch-vim directory. After that »make install« succeeded. But the main obstacle is that new users must find by trial and error that they must type »s« to search, »t« to tag files and so on. It would be very useful to list all the key bindings and configuration options. I still don't know how to compose a new message. https://github.com/imain/notmuch-vim/blob/master/doc/notmuch.txt Look at the 'MAPPINGS' section. I think a tutorial might be good though. Well like any other text based client it uses a text dump of a links or such (I forget which one). I think »elinks --dump« We have great support for web browser viewing of the email though if that isn't good enough. Yes, it's 2014. ;-) Time flies!! Let me know if you have suggestions on where it's lacking. Okay, thank you so far. Thanks! ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch