emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
From: Adam Spiers <orgmode@adamspiers.org>
To: emacs-orgmode@gnu.org
Subject: Integration of Org mode and mairix (was: ... and Gnus)
Date: Fri, 20 Jul 2007 17:08:33 +0100	[thread overview]
Message-ID: <20070720160833.GD28297@atlantic.linksys.moosehall> (raw)
In-Reply-To: <m3lkdec8ej.fsf@cerebro.fsfeurope.org>

[-- Attachment #1: Type: text/plain, Size: 2765 bytes --]

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
         <mairix://m:4679BAC0.4057103@spiers.net>

  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.

       <mairix://s:orgmode>

    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

[-- Attachment #2: as-gtd.el --]
[-- Type: text/plain, Size: 2258 bytes --]

;; 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 <display-message>\"' &"
  "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 "<mairix://")
        (forward-char 1)
      (skip-chars-backward "^<"))
    (if (looking-at "mairix://\\([^>]+?\\)>")
        (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)))))))

[-- Attachment #3: mairix-copy-message --]
[-- Type: text/plain, Size: 1741 bytes --]

#!/usr/bin/perl
#
# extract headers from mail stream on STDIN.
# For use with mutt, .e.g
#
#    macro index "\em"  ":set pipe_decode=no\n<pipe-message>mairix-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 <<EOUSAGE;
Usage: cat mail1 [mail2 ...] | $me [--ids] [--threads]
Options:
  -h, --help     Show this help
      --ids      Extract Message-ID header(s)
      --threads  Extract thread subject(s)
EOUSAGE
}

my @found;

my $subject;
while (<STDIN>) {
  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 = <STDIN>;
  exit 1;
}

open(FAKECLIP, ">$ENV{HOME}/.clip-mairix")
  or die "Couldn't open($ENV): $!\n";

foreach (@found) {
  my $URL = "<mairix://$_>";
  print FAKECLIP "$URL\n";
  print          "$URL\n";
}

close(FAKECLIP);

# print "Press enter to continue ... ";
# my $read_key = <STDIN>;
sleep 1;

exit 0; # for mutt wait_key

[-- Attachment #4: Mairix.pm --]
[-- Type: text/plain, Size: 1289 bytes --]

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<mairix-wrapper>, L<mairix-copy-message>

=cut

1;

[-- Attachment #5: Type: text/plain, Size: 149 bytes --]

_______________________________________________
Emacs-orgmode mailing list
Emacs-orgmode@gnu.org
http://lists.gnu.org/mailman/listinfo/emacs-orgmode

  reply	other threads:[~2007-07-20 16:08 UTC|newest]

Thread overview: 65+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-07-17 19:22 Integration of Org mode and Gnus Georg C. F. Greve
2007-07-18  0:22 ` Jason F. McBrayer
2007-07-18  8:03   ` Georg C. F. Greve
2007-07-18 10:04     ` Bastien
2007-07-18 10:29       ` Georg C. F. Greve
2007-07-20 16:08         ` Adam Spiers [this message]
2007-07-22 23:15           ` Integration of Org mode and mairix Georg C. F. Greve
2007-07-23  2:24             ` Is it any function similar to appoinment alert in the planner mode? brianjiang
2007-07-23 12:38               ` Bernt Hansen
2007-07-25 19:15               ` Bastien Guerry
2007-07-26  7:23                 ` Bastien
2007-07-27  5:17                   ` brianjiang
2007-08-07  0:05                   ` Bastien
2007-08-07 17:13                     ` Bastien
2007-08-21  2:37                     ` william
2007-07-24 14:38             ` Integration of Org mode and mairix Bastien
2007-07-30 14:02               ` Georg C. F. Greve
2007-07-30 16:02                 ` Bastien
2007-07-30 16:31                   ` Leo
2007-07-30 17:26                     ` Bastien
2007-08-05  1:32                       ` Xiao-Yong Jin
2007-07-31 15:52                   ` Jason F. McBrayer
2007-07-31 16:56                   ` Xiao-Yong Jin
2007-07-31 17:19                     ` Georg C. F. Greve
2007-08-05  1:40                       ` Xiao-Yong Jin
2007-07-31 18:43                     ` Bastien
2007-08-01 14:52                       ` Jason F. McBrayer
2007-08-01 16:59                         ` Bastien
2007-08-05  1:44                         ` Xiao-Yong Jin
2007-08-05 10:20                           ` Georg C. F. Greve
2007-08-07 17:54                             ` Bastien
2007-08-10  6:56                               ` Carsten Dominik
2007-08-14 11:29                                 ` Adam Spiers
2007-08-15 17:46                                   ` Carsten Dominik
2007-08-21 12:38                                   ` Carsten Dominik
2007-08-14 22:54                                 ` Steven Lumos
2007-08-15 17:46                                   ` Carsten Dominik
2007-08-16 18:48                                     ` Steven Lumos
2007-09-23 15:44                                 ` Georg C. F. Greve
2007-09-23 18:12                                   ` Carsten Dominik
2007-09-23 21:50                                     ` Georg C. F. Greve
2007-09-23 22:05                                       ` Carsten Dominik
2007-09-24 17:38                                         ` Georg C. F. Greve
2007-09-25 14:41                                           ` Carsten Dominik
2007-09-25 22:25                                             ` Georg C. F. Greve
2007-09-25 22:38                                               ` Carsten Dominik
2007-07-18 11:04       ` Integration of Org mode and Gnus Jason F. McBrayer
2007-07-18 12:01         ` Bastien
2007-07-18 12:57           ` Jason F. McBrayer
2007-07-18 13:08           ` Georg C. F. Greve
2007-07-18 15:39             ` Bastien
2007-07-18 19:39               ` Georg C. F. Greve
2007-07-18 22:06                 ` Bastien
2007-07-19 15:29                   ` Georg C. F. Greve
2007-07-18 21:54               ` Carsten Dominik
2007-07-19 15:41                 ` Bastien
2007-07-18 10:20     ` Tassilo Horn
2007-07-18 10:35       ` Georg C. F. Greve
2007-07-18 11:55         ` Tassilo Horn
2007-07-18 12:25           ` Georg C. F. Greve
2007-07-18 12:54             ` Tassilo Horn
2007-07-18 13:18               ` Georg C. F. Greve
2007-07-18 13:32                 ` Tassilo Horn
2007-07-18  0:55 ` Bastien
2007-07-18  7:53   ` Georg C. F. Greve

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.orgmode.org/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20070720160833.GD28297@atlantic.linksys.moosehall \
    --to=orgmode@adamspiers.org \
    --cc=emacs-orgmode@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).