From mboxrd@z Thu Jan 1 00:00:00 1970 From: Adam Spiers Subject: Integration of Org mode and mairix (was: ... and Gnus) Date: Fri, 20 Jul 2007 17:08:33 +0100 Message-ID: <20070720160833.GD28297@atlantic.linksys.moosehall> References: <87r6n6ni2j.fsf@bzg.ath.cx> Reply-To: Adam Spiers Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="TB36FDmn/VVEgNH/" Return-path: Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1IBv1x-0006L8-8p for emacs-orgmode@gnu.org; Fri, 20 Jul 2007 12:08:37 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1IBv1w-0006Iy-36 for emacs-orgmode@gnu.org; Fri, 20 Jul 2007 12:08:36 -0400 Received: from [199.232.76.173] (helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1IBv1v-0006IM-NV for emacs-orgmode@gnu.org; Fri, 20 Jul 2007 12:08:35 -0400 Received: from mail.beimborn.com ([70.84.38.100]) by monty-python.gnu.org with esmtps (TLS-1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.60) (envelope-from ) id 1IBv1v-00020E-6g for emacs-orgmode@gnu.org; Fri, 20 Jul 2007 12:08:35 -0400 Received: from mail.beimborn.com (localhost.localdomain [127.0.0.1]) by mail.beimborn.com (8.12.11.20060308/8.12.8) with ESMTP id l6KG8YDb016909 for ; Fri, 20 Jul 2007 11:08:34 -0500 Received: from localhost (localhost [[UNIX: localhost]]) by mail.beimborn.com (8.12.11.20060308/8.12.11/Submit) id l6KG8YuQ016897 for emacs-orgmode@gnu.org; Fri, 20 Jul 2007 17:08:34 +0100 Content-Disposition: inline In-Reply-To: List-Id: "General discussions about Org-mode." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: emacs-orgmode-bounces+geo-emacs-orgmode=m.gmane.org@gnu.org Errors-To: emacs-orgmode-bounces+geo-emacs-orgmode=m.gmane.org@gnu.org To: emacs-orgmode@gnu.org --TB36FDmn/VVEgNH/ Content-Type: text/plain; charset=us-ascii Content-Disposition: inline On Wed, Jul 18, 2007 at 12:29:08PM +0200, Georg C. F. Greve wrote: > Since I know which group I am asking to move things to, being able to > link to the message ID directly would definitely solve the problem, and > maybe even more elegantly, as it should be robust against resorting the > mail group. Funnily enough, I implemented exactly this idea about 15 months ago, except that my implementation uses mairix (the mail indexer), and is mode-agnostic. I use mutt as my mail reader, so the usage workflow is this: 1. Read a mail which requires a TODO action 2. Hit a keypress combo which triggers a Perl script acting on the mail to extract the Message-ID and dump it to a predictably-named temporary file, which can be considered as a dedicated clipboard. 3. In emacs, hit another keypress combo to yank the contents of the clipboard into a new item in my TODO.org file as a link, e.g.: * NEXTACTION [#B] read this email about blah blah 4. Archive the mail anywhere, safe in the knowledge that later on, moving the point over this mairix:// link within emacs and pressing another keypress combo will perform a mairix search for this Message-ID, and launch a mutt window displaying the mail, regardless of wherever it's been archived. At first I thought that this was just a cute little trick, but it's proved itself to be an absolute god-send: - emails can be converted into TODO items and cleared out of my inbox within seconds. Anyone trying to follow GTD already knows the huge value of being able to quickly process an inbox. - Having mail linked to like this (and in general having it indexed and instantly searchable) means that I no longer have to make sure mails are carefully archived in the right folder according to topic. This used to be very time-consuming, and didn't always work, e.g. when a single mail covered more than one topic. These days, I simply have one archive folder per month to keep folder sizes manageable. - The URL-like syntax allows me to store hyperlinks which trigger more complex queries rather than just searching for a single message id, e.g. would find all mails with 'orgmode' in the subject. I made it mode-agnostic because I also wanted to be able to link to mails from within source code comments, log files, muse-mode, etc. Having said that, if there is a generic mode-agnostic hyperlinking framework in emacs, I really should have my wrist slapped for reinventing the wheel. In particular, I do wish that org-mode and muse-mode had the same hyperlinking syntax ... Anyway, source code for my hacks attached. Cheers, Adam --TB36FDmn/VVEgNH/ Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="as-gtd.el" ;; Things to help me with Getting Things Done. (defvar as-mairix-links-clipboard "~/.clip-mairix" "Pseudo-clipboard file where mairix URLs get copied to.") (defvar as-mairix-results-folder "~/mail/mairix" "Folder where mairix writes results.") (defvar as-mairix-link-viewer-command "mairix '%search%' && xterm -title 'mairix search: %search%' -e 'unset COLUMNS; mutt -F ~/.mutt/work -f %folder% -e \"push \"' &" "Command to view messages linked to by 'mairix://...' links.") (defun as-mairix-yank-links () "Yank from file defined by `as-mairix-links-clipboard'." (interactive) (let ((bytes (cadr (insert-file-contents (expand-file-name as-mairix-links-clipboard))))) (forward-char bytes) (save-excursion (forward-char -1) (if (looking-at "\n") (delete-char 1))))) (defun as-mairix-search-at-point () "Return the start and end points of a mairix link at the current point. The result is a paired list of character positions for a mairix link located at the current point in the current buffer." (save-excursion (if (looking-at "]+?\\)>") (match-string 1) nil))) (defun as-mairix-view-link-at-point (show-async-buf) "View the 'mairix://...' link at the point using the shell command defined by `as-mairix-link-viewer-command'." (interactive "P") (let ((message-id (as-mairix-search-at-point))) (or message-id (error "No mairix URL found at point")) (let ((cmd as-mairix-link-viewer-command)) (while (string-match "%search%" cmd) (setq cmd (replace-match message-id 'fixedcase 'literal cmd))) (while (string-match "%folder%" cmd) (setq cmd (replace-match as-mairix-results-folder 'fixedcase 'literal cmd))) ;; By default, async shell-command invocations display the temp ;; buffer, which is annoying here. We choose a deterministic ;; buffer name so we can hide it again immediately., (let ((tmpbufname (generate-new-buffer-name " *mairix-view*"))) (shell-command cmd tmpbufname) (or show-async-buf (delete-windows-on (get-buffer tmpbufname))))))) --TB36FDmn/VVEgNH/ Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename=mairix-copy-message #!/usr/bin/perl # # extract headers from mail stream on STDIN. # For use with mutt, .e.g # # macro index "\em" ":set pipe_decode=no\nmairix-copy-message ids\n:set pipe_decode\n" use strict; use warnings; use Getopt::Long; use Mairix; my %opts; GetOptions( \%opts, 'help', 'ids', 'threads', ) or usage(); usage() if @ARGV or $opts{help}; sub usage { warn @_, "\n" if @_; (my $me = $0) =~ s,.*/,,; die <) { chomp; if ($opts{ids} && /^message-id:\s+(.+)\s*$/i) { my $value = $1; if ($value =~ /^<(.+)>$/) { push @found, "m:$1"; } else { warn "WARNING: Message-ID '$value' violates RFC.\n"; push @found, "m:$value"; } next; } if ($opts{threads}) { if ($subject) { if (/^\s+(.+)/) { # continuation line $subject .= " " . $1; next; } # next header push @found, join ' ', Mairix::subject_query($subject); undef $subject; next; } elsif (/^Subject:\s+(.+)\s*$/i) { $subject = $1; } } } # xclip doesn't work :-( if (! @found) { warn "No Message-ID header found! Press a key...\n"; my $read_key = ; exit 1; } open(FAKECLIP, ">$ENV{HOME}/.clip-mairix") or die "Couldn't open($ENV): $!\n"; foreach (@found) { my $URL = ""; print FAKECLIP "$URL\n"; print "$URL\n"; } close(FAKECLIP); # print "Press enter to continue ... "; # my $read_key = ; sleep 1; exit 0; # for mutt wait_key --TB36FDmn/VVEgNH/ Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="Mairix.pm" package Mairix; =head1 NAME Mairix - helpers for mairix CLI utilities =head1 SYNOPSIS TODO =head1 DESCRIPTION TODO =cut use strict; use warnings; =head2 subject_query Turn a subject into a mairix query to retrieve mail's with related subjects. =cut sub subject_query { my ($subject) = @_; my $query = normalize_subject($subject); $query =~ s/^\s+//g; $query =~ s/\s+$//g; my @tokens = split /\s+|[^\w]+/, $query; # return map "s:$_", grep { length($_) && /\w/ } @tokens; return 's:' . join ",", grep { length($_) && /\w/ } @tokens; } =head2 normalize_subject Trim a subject of cruft accumulated as a result of replying to/forwarding the message etc. =cut sub normalize_subject { my ($original) = @_; my $new = $original; 1 while $new =~ s/^\s+// or $new =~ s/\s+$// or $new =~ s/^((Re|Fwd|Aw|Antw|Svar):|\(Fwd\))\s*//i or $new =~ s/^\[Fwd:\s*(.+)\s*\]$/$1/i or $new =~ s/\(Fwd\)$//i or $new =~ s/^(FYI|FYEO|FCP|AHOD|F)(:|\b)//i or $new =~ s/^\s*\[[\w -]+\]\s+//i or $new =~ s/\bv\d+(\.\d+)*(\b|$)//i; if ($new ne $original) { warn "Normalized '$original' to '$new'\n"; } return $new; } =head1 BUGS None, zero, nada etc. =head1 SEE ALSO L, L =cut 1; --TB36FDmn/VVEgNH/ Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: inline _______________________________________________ Emacs-orgmode mailing list Emacs-orgmode@gnu.org http://lists.gnu.org/mailman/listinfo/emacs-orgmode --TB36FDmn/VVEgNH/--