<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	>
<channel>
	<title>Comments on: GTD and Mutt</title>
	<atom:link href="http://docwhat.gerf.org/2007/10/gtd-and-mutt/feed/" rel="self" type="application/rss+xml" />
	<link>http://docwhat.gerf.org/2007/10/gtd-and-mutt/</link>
	<description>Some men are discovered; others are found out</description>
	<pubDate>Mon, 08 Sep 2008 08:11:01 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.6.1</generator>
		<item>
		<title>By: Klaus Weidner</title>
		<link>http://docwhat.gerf.org/2007/10/gtd-and-mutt/#comment-3941</link>
		<dc:creator>Klaus Weidner</dc:creator>
		<pubDate>Fri, 14 Dec 2007 05:41:20 +0000</pubDate>
		<guid isPermaLink="false">http://docwhat.gerf.org/2007/10/gtd-and-mutt/#comment-3941</guid>
		<description>Hello,

thanks for the writeup, this is very close to what I was looking for.

I've been hacking on it a bit to add label completion with the Tab key, and a prompt when using new labels to check if you want to add them to the label list (to avoid typos).

I'm keeping everything in my Inbox (including my own sent messages) and adding labels like "Done" to messages I don't want to see in the default view (!~y Done), but they are still available for other searches.

Also, I've used a dirty hack to make the label function work for multiple tagged messages by saving the actions to do (add/remove/set labels) to a temporary file.

Replace "Mutt-edit" with vim/emacs/editor of your choice, and note that I've changed the paths.

&lt;b&gt;***** .muttrc&lt;/b&gt;

&lt;pre&gt;macro index,pager y "&#60;enter-command&gt;set editor=\"editlabel\"\n\
&#60;shell-escape&gt;rm -f ~/.label_saved_action&#60;enter&gt;\
&#60;tag-prefix&gt;&#60;edit&gt;\
&#60;sync-mailbox&gt;&#60;next-undeleted&gt;\
&#60;shell-escape&gt;rm -f ~/.label_saved_action&#60;enter&gt;\
&#60;enter-command&gt;set editor=Mutt-edit\n" "Edit Label"&lt;/pre&gt;

&lt;b&gt;**** editlabel&lt;/b&gt;

&lt;pre&gt;#!/usr/bin/python -utWall

import readline, sys, os

histfile = os.path.join(os.environ["HOME"], ".label_history")
labelfile = os.path.join(os.environ["HOME"], ".labels")
actfile = os.path.join(os.environ["HOME"], ".label_saved_action")

def get_label_file():
   try:
       return map(lambda s: s.rstrip(), open(labelfile, 'r').readlines())
   except IOError:
       return

def label_completions(text, state):
   labels = get_label_file()
   candidates = filter(lambda x: x.startswith(text), labels)
   try:
       return candidates[state]
   except:
       return

def my_input(prompt, default=None, completions=None):
   if default is not None:
       def pre_input_hook():
           readline.insert_text(str(default))
           readline.redisplay()
       readline.set_pre_input_hook(pre_input_hook)
       val = []

   readline.parse_and_bind("tab: complete")
   readline.set_completer(completions)
   return raw_input(prompt)

def get_label(filename):
   header = 'X-Label: '
   fp = file(filename, 'r')
   result = ''
   for line in fp.readlines():
       if line.startswith(header):
           result = line[len(header):].strip()
           break
   fp.close()
   return result

def write_label(filename, label):
   header = ['X-Label:']
   tmpfile = filename + '.tmp'
   if label:
       header.append(' ')
       header.append(label.rstrip().lstrip())
   header = '"%s"' % ((''.join(header)).replace('"', '\"'))
   cmd = ' '.join([ 'formail',
                    '-I',
                    header,
                    '&#60;',
                    filename,
                    '&gt;',
                    tmpfile,
                    '&#38;&#38;',
                    'mv',
                    tmpfile,
                    filename
                    ])
   os.system(cmd)

def ask(prompt, default):
   print prompt + ' ['+default+'] ',
   ans = sys.stdin.readline().rstrip()
   if ans == "":
       return default
   return ans

def edit_label(label):
   while True:
       label = my_input('Label: ', label, label_completions)
       known_labels = set(get_label_file())
       unknown = filter(lambda x: not x in known_labels, label.split())
       if len(unknown) == 0:
           break
       else:
           not_added=0
           for new in unknown:
               if ask_yn("label '"+new+"' not known, add?", 'n'):
                   open(labelfile, "a").write(new+'\n')
               else:
                   not_added += 1
           if not_added==0:
               break

   return label

def do_action(action, infile, new_label=None):
   if action == 'a' or action == '+':
       if new_label == None:
           new_label = edit_label("")
       current_labels = get_label(infile)
       updated_labels = current_labels
       for new in new_label.split():
           if not new in set(current_labels.split()):
               updated_labels += ' ' + new
       if updated_labels != current_labels:
           write_label(infile, updated_labels)
       return new_label

   elif action == 'r' or action == '-':
       rm_label = new_label
       if rm_label == None:
           rm_label = edit_label("")
       current_labels = get_label(infile)
       updated_labels = ""
       for new in current_labels.split():
           if not new in set(rm_label.split()):
               updated_labels += ' ' + new
       if updated_labels != current_labels:
           write_label(infile, updated_labels)
       return rm_label

   elif action == 'e' or action == '=':
       label = get_label(infile)
       if new_label == None:
           new_label = edit_label(label)
       if new_label != label:
           write_label(infile, new_label)
       return new_label

def ask_yn(prompt, default):
   return ask(prompt, default) == 'y'

if "__main__" == __name__:
   infile = sys.argv[1]

   if hasattr(readline, 'read_history_file'):
       try:
           readline.read_history_file(histfile)
       except IOError:
           pass

   try:
       actions = open(actfile, 'r').readlines()
       action = actions[0].rstrip()
       new_label = actions[1].rstrip()
       do_action(action, infile, new_label)
   except IOError:
       # ask user
       os.system("clear")
       action = ask("(a)dd, (r)emove, or (e)dit labels?", "a")
       new_label = do_action(action, infile)

       open(actfile, 'w').write(action + '\n' + new_label + '\n')
       readline.write_history_file(histfile)
&lt;/pre&gt;</description>
		<content:encoded><![CDATA[<p>Hello,</p>
<p>thanks for the writeup, this is very close to what I was looking for.</p>
<p>I&#8217;ve been hacking on it a bit to add label completion with the Tab key, and a prompt when using new labels to check if you want to add them to the label list (to avoid typos).</p>
<p>I&#8217;m keeping everything in my Inbox (including my own sent messages) and adding labels like &#8220;Done&#8221; to messages I don&#8217;t want to see in the default view (!~y Done), but they are still available for other searches.</p>
<p>Also, I&#8217;ve used a dirty hack to make the label function work for multiple tagged messages by saving the actions to do (add/remove/set labels) to a temporary file.</p>
<p>Replace &#8220;Mutt-edit&#8221; with vim/emacs/editor of your choice, and note that I&#8217;ve changed the paths.</p>
<p><b>***** .muttrc</b></p>
<pre>macro index,pager y "&lt;enter-command>set editor=\"editlabel\"\n\
&lt;shell-escape>rm -f ~/.label_saved_action&lt;enter>\
&lt;tag-prefix>&lt;edit>\
&lt;sync-mailbox>&lt;next-undeleted>\
&lt;shell-escape>rm -f ~/.label_saved_action&lt;enter>\
&lt;enter-command>set editor=Mutt-edit\n" "Edit Label"</pre>
<p><b>**** editlabel</b></p>
<pre>#!/usr/bin/python -utWall

import readline, sys, os

histfile = os.path.join(os.environ["HOME"], ".label_history")
labelfile = os.path.join(os.environ["HOME"], ".labels")
actfile = os.path.join(os.environ["HOME"], ".label_saved_action")

def get_label_file():
   try:
       return map(lambda s: s.rstrip(), open(labelfile, 'r').readlines())
   except IOError:
       return

def label_completions(text, state):
   labels = get_label_file()
   candidates = filter(lambda x: x.startswith(text), labels)
   try:
       return candidates[state]
   except:
       return

def my_input(prompt, default=None, completions=None):
   if default is not None:
       def pre_input_hook():
           readline.insert_text(str(default))
           readline.redisplay()
       readline.set_pre_input_hook(pre_input_hook)
       val = []

   readline.parse_and_bind("tab: complete")
   readline.set_completer(completions)
   return raw_input(prompt)

def get_label(filename):
   header = 'X-Label: '
   fp = file(filename, 'r')
   result = ''
   for line in fp.readlines():
       if line.startswith(header):
           result = line[len(header):].strip()
           break
   fp.close()
   return result

def write_label(filename, label):
   header = ['X-Label:']
   tmpfile = filename + '.tmp'
   if label:
       header.append(' ')
       header.append(label.rstrip().lstrip())
   header = '"%s"' % ((''.join(header)).replace('"', '\"'))
   cmd = ' '.join([ 'formail',
                    '-I',
                    header,
                    '&lt;',
                    filename,
                    '>',
                    tmpfile,
                    '&amp;&amp;',
                    'mv',
                    tmpfile,
                    filename
                    ])
   os.system(cmd)

def ask(prompt, default):
   print prompt + ' ['+default+'] ',
   ans = sys.stdin.readline().rstrip()
   if ans == "":
       return default
   return ans

def edit_label(label):
   while True:
       label = my_input('Label: ', label, label_completions)
       known_labels = set(get_label_file())
       unknown = filter(lambda x: not x in known_labels, label.split())
       if len(unknown) == 0:
           break
       else:
           not_added=0
           for new in unknown:
               if ask_yn("label '"+new+"' not known, add?", 'n'):
                   open(labelfile, "a").write(new+'\n')
               else:
                   not_added += 1
           if not_added==0:
               break

   return label

def do_action(action, infile, new_label=None):
   if action == 'a' or action == '+':
       if new_label == None:
           new_label = edit_label("")
       current_labels = get_label(infile)
       updated_labels = current_labels
       for new in new_label.split():
           if not new in set(current_labels.split()):
               updated_labels += ' ' + new
       if updated_labels != current_labels:
           write_label(infile, updated_labels)
       return new_label

   elif action == 'r' or action == '-':
       rm_label = new_label
       if rm_label == None:
           rm_label = edit_label("")
       current_labels = get_label(infile)
       updated_labels = ""
       for new in current_labels.split():
           if not new in set(rm_label.split()):
               updated_labels += ' ' + new
       if updated_labels != current_labels:
           write_label(infile, updated_labels)
       return rm_label

   elif action == 'e' or action == '=':
       label = get_label(infile)
       if new_label == None:
           new_label = edit_label(label)
       if new_label != label:
           write_label(infile, new_label)
       return new_label

def ask_yn(prompt, default):
   return ask(prompt, default) == 'y'

if "__main__" == __name__:
   infile = sys.argv[1]

   if hasattr(readline, 'read_history_file'):
       try:
           readline.read_history_file(histfile)
       except IOError:
           pass

   try:
       actions = open(actfile, 'r').readlines()
       action = actions[0].rstrip()
       new_label = actions[1].rstrip()
       do_action(action, infile, new_label)
   except IOError:
       # ask user
       os.system("clear")
       action = ask("(a)dd, (r)emove, or (e)dit labels?", "a")
       new_label = do_action(action, infile)

       open(actfile, 'w').write(action + '\n' + new_label + '\n')
       readline.write_history_file(histfile)
</pre>
]]></content:encoded>
	</item>
	<item>
		<title>By: sr</title>
		<link>http://docwhat.gerf.org/2007/10/gtd-and-mutt/#comment-3905</link>
		<dc:creator>sr</dc:creator>
		<pubDate>Sun, 18 Nov 2007 15:24:26 +0000</pubDate>
		<guid isPermaLink="false">http://docwhat.gerf.org/2007/10/gtd-and-mutt/#comment-3905</guid>
		<description>Thank you! Very useful, even outside of GTD: just for the labels.

&#62;I like my editlabel better
The same. But cannot point to anything specific (maybe readline).

&#62;But that narrow-wide trick is neat.
What is it?

&#62;evil os.system()
Maybe a comment about the dependancy on formail command could help?

&#62;I haven’t added tab-completion from the &#62;history; I’m not sure if I will or not.
If this is not too much work, it could be appreciated: for emacs users, tab completion feels natural.

Minor comment: instead of duplicating the command in the muttrc, one can use:
macro index,pager</description>
		<content:encoded><![CDATA[<p>Thank you! Very useful, even outside of GTD: just for the labels.</p>
<p>&gt;I like my editlabel better<br />
The same. But cannot point to anything specific (maybe readline).</p>
<p>&gt;But that narrow-wide trick is neat.<br />
What is it?</p>
<p>&gt;evil os.system()<br />
Maybe a comment about the dependancy on formail command could help?</p>
<p>&gt;I haven’t added tab-completion from the &gt;history; I’m not sure if I will or not.<br />
If this is not too much work, it could be appreciated: for emacs users, tab completion feels natural.</p>
<p>Minor comment: instead of duplicating the command in the muttrc, one can use:<br />
macro index,pager</p>
]]></content:encoded>
	</item>
</channel>
</rss>
