<?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"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
		>
<channel>
	<title>Comments on: GTD and Mutt</title>
	<atom:link href="http://docwhat.org/2007/10/gtd-and-mutt/feed/" rel="self" type="application/rss+xml" />
	<link>http://docwhat.org/2007/10/gtd-and-mutt/</link>
	<description>Some men are discovered; others are found out</description>
	<lastBuildDate>Fri, 13 Aug 2010 15:37:58 +0000</lastBuildDate>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	
	<item>
		<title>By: Klaus Weidner</title>
		<link>http://docwhat.org/2007/10/gtd-and-mutt/comment-page-1/#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&#039;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&#039;m keeping everything in my Inbox (including my own sent messages) and adding labels like &quot;Done&quot; to messages I don&#039;t want to see in the default view (!~y Done), but they are still available for other searches.

Also, I&#039;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 &quot;Mutt-edit&quot; with vim/emacs/editor of your choice, and note that I&#039;ve changed the paths.

&lt;b&gt;***** .muttrc&lt;/b&gt;

[code]
macro index,pager y &quot;&lt;enter-command&gt;set editor=\&quot;editlabel\&quot;\n\
&lt;shell-escape&gt;rm -f ~/.label_saved_action&lt;enter&gt;\
&lt;tag-prefix&gt;&lt;edit&gt;\
&lt;sync-mailbox&gt;&lt;next-undeleted&gt;\
&lt;shell-escape&gt;rm -f ~/.label_saved_action&lt;enter&gt;\
&lt;enter-command&gt;set editor=Mutt-edit\n&quot; &quot;Edit Label&quot;
[/code]

&lt;b&gt;**** editlabel&lt;/b&gt;

[code lang=&quot;python&quot;]
#!/usr/bin/python -utWall

import readline, sys, os

histfile = os.path.join(os.environ[&quot;HOME&quot;], &quot;.label_history&quot;)
labelfile = os.path.join(os.environ[&quot;HOME&quot;], &quot;.labels&quot;)
actfile = os.path.join(os.environ[&quot;HOME&quot;], &quot;.label_saved_action&quot;)

def get_label_file():
   try:
       return map(lambda s: s.rstrip(), open(labelfile, &#039;r&#039;).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(&quot;tab: complete&quot;)
   readline.set_completer(completions)
   return raw_input(prompt)

def get_label(filename):
   header = &#039;X-Label: &#039;
   fp = file(filename, &#039;r&#039;)
   result = &#039;&#039;
   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 = [&#039;X-Label:&#039;]
   tmpfile = filename + &#039;.tmp&#039;
   if label:
       header.append(&#039; &#039;)
       header.append(label.rstrip().lstrip())
   header = &#039;&quot;%s&quot;&#039; % ((&#039;&#039;.join(header)).replace(&#039;&quot;&#039;, &#039;\&quot;&#039;))
   cmd = &#039; &#039;.join([ &#039;formail&#039;,
                    &#039;-I&#039;,
                    header,
                    &#039;&lt;&#039;,
                    filename,
                    &#039;&gt;&#039;,
                    tmpfile,
                    &#039;&amp;&amp;&#039;,
                    &#039;mv&#039;,
                    tmpfile,
                    filename
                    ])
   os.system(cmd)

def ask(prompt, default):
   print prompt + &#039; [&#039;+default+&#039;] &#039;,
   ans = sys.stdin.readline().rstrip()
   if ans == &quot;&quot;:
       return default
   return ans

def edit_label(label):
   while True:
       label = my_input(&#039;Label: &#039;, 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(&quot;label &#039;&quot;+new+&quot;&#039; not known, add?&quot;, &#039;n&#039;):
                   open(labelfile, &quot;a&quot;).write(new+&#039;\n&#039;)
               else:
                   not_added += 1
           if not_added==0:
               break

   return label

def do_action(action, infile, new_label=None):
   if action == &#039;a&#039; or action == &#039;+&#039;:
       if new_label == None:
           new_label = edit_label(&quot;&quot;)
       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 += &#039; &#039; + new
       if updated_labels != current_labels:
           write_label(infile, updated_labels)
       return new_label

   elif action == &#039;r&#039; or action == &#039;-&#039;:
       rm_label = new_label
       if rm_label == None:
           rm_label = edit_label(&quot;&quot;)
       current_labels = get_label(infile)
       updated_labels = &quot;&quot;
       for new in current_labels.split():
           if not new in set(rm_label.split()):
               updated_labels += &#039; &#039; + new
       if updated_labels != current_labels:
           write_label(infile, updated_labels)
       return rm_label

   elif action == &#039;e&#039; or action == &#039;=&#039;:
       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) == &#039;y&#039;

if &quot;__main__&quot; == __name__:
   infile = sys.argv[1]

   if hasattr(readline, &#039;read_history_file&#039;):
       try:
           readline.read_history_file(histfile)
       except IOError:
           pass

   try:
       actions = open(actfile, &#039;r&#039;).readlines()
       action = actions[0].rstrip()
       new_label = actions[1].rstrip()
       do_action(action, infile, new_label)
   except IOError:
       # ask user
       os.system(&quot;clear&quot;)
       action = ask(&quot;(a)dd, (r)emove, or (e)dit labels?&quot;, &quot;a&quot;)
       new_label = do_action(action, infile)

       open(actfile, &#039;w&#039;).write(action + &#039;\n&#039; + new_label + &#039;\n&#039;)
       readline.write_history_file(histfile)
[/code]
</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 class="brush: plain;">
macro index,pager y &quot;&lt;enter-command&gt;set editor=\&quot;editlabel\&quot;\n\
&lt;shell-escape&gt;rm -f ~/.label_saved_action&lt;enter&gt;\
&lt;tag-prefix&gt;&lt;edit&gt;\
&lt;sync-mailbox&gt;&lt;next-undeleted&gt;\
&lt;shell-escape&gt;rm -f ~/.label_saved_action&lt;enter&gt;\
&lt;enter-command&gt;set editor=Mutt-edit\n&quot; &quot;Edit Label&quot;
</pre>
<p><b>**** editlabel</b></p>
<pre class="brush: python;">
#!/usr/bin/python -utWall

import readline, sys, os

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

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(&quot;tab: complete&quot;)
   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 = '&quot;%s&quot;' % ((''.join(header)).replace('&quot;', '\&quot;'))
   cmd = ' '.join([ 'formail',
                    '-I',
                    header,
                    '&lt;',
                    filename,
                    '&gt;',
                    tmpfile,
                    '&amp;&amp;',
                    'mv',
                    tmpfile,
                    filename
                    ])
   os.system(cmd)

def ask(prompt, default):
   print prompt + ' ['+default+'] ',
   ans = sys.stdin.readline().rstrip()
   if ans == &quot;&quot;:
       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(&quot;label '&quot;+new+&quot;' not known, add?&quot;, 'n'):
                   open(labelfile, &quot;a&quot;).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(&quot;&quot;)
       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(&quot;&quot;)
       current_labels = get_label(infile)
       updated_labels = &quot;&quot;
       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 &quot;__main__&quot; == __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(&quot;clear&quot;)
       action = ask(&quot;(a)dd, (r)emove, or (e)dit labels?&quot;, &quot;a&quot;)
       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.org/2007/10/gtd-and-mutt/comment-page-1/#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.

&gt;I like my editlabel better
The same. But cannot point to anything specific (maybe readline).

&gt;But that narrow-wide trick is neat.
What is it?

&gt;evil os.system()
Maybe a comment about the dependancy on formail command could help?

&gt;I haven’t added tab-completion from the &gt;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>
