sup

A curses threads-with-tags style email client

sup.git

git clone https://supmua.dev/git/sup/
commit ff8ce104f85ae3a236c1c5dbf2156cd9593c6d3b
parent d9aad433dff55af342d17e70d26c7eb54169643b
Author: Marcus Williams <marcus-sup@bar-coded.net>
Date:   Sat, 22 Dec 2007 15:49:53 -0800

additional gmail-style query language additions
original message:
Simple patch attached. Adds the "during" operator to the date searches
so you can say during:today or during:november . I prefer it to in/on
today :)

Also adds an alias for the label search of "is" so that you can do
more gmail like searches is:starred, is:unread, is:spam, is:deleted.
The last two required changing the return value to a list/pair in
parse_user_string_query and I'm not sure what the best way to do this
in ruby is. For now its a simple list. Also is there a better way to
deal with a nil result other than returning [nil,nil]? The first
element is the parsed query string, the second allows the parser to
add options to the query like :load_spam, :load_deleted which are then
used in the code already to drop (or not) the spam/deleted labeled
messages.

I figured you'd never want to search killed threads... and it made the
patch nastier because of the :skip_killed flag.

I'd like to add a has:attachment operator but the ferret index doesnt
appear to store enough info for this. It would be really nice to be
able to search for attachments (of certain types as well with
filename:...) I took a look at extending the index to add a new field
"filename" that contains a list of filenames attached to a message,
but this would break everyones index so I'm just requesting it as a
wishlist item rather than submitting a patch. With it you could do
wildcard filename attachment searches and the has:attachment search
would just be converted to a search for the presence of any filename.

Diffstat:
M lib/sup/index.rb | 26 ++++++++++++++++++++++++--
M lib/sup/modes/search-results-mode.rb | 13 ++++++++-----
2 files changed, 32 insertions(+), 7 deletions(-)
diff --git a/lib/sup/index.rb b/lib/sup/index.rb
@@ -394,6 +394,7 @@ protected
   ## do any specialized parsing
   ## returns nil and flashes error message if parsing failed
   def parse_user_query_string str
+    extraopts = {}
     result = str.gsub(/\b(to|from):(\S+)\b/) do
       field, name = $1, $2
       if(p = ContactManager.contact_for(name))
@@ -405,9 +406,26 @@ protected
       end.join(":")
     end
     
+    # gmail style "is" operator
+    result = result.gsub(/\b(is):(\S+)\b/) do
+      field, label = $1, $2
+      case label
+      when "read"
+        "-label:unread"
+      when "spam"
+        extraopts[:load_spam] = true
+        "label:spam"
+      when "deleted"
+        extraopts[:load_deleted] = true
+        "label:deleted"
+      else
+        "label:#{$2}"
+      end
+    end
+
     if $have_chronic
       chronic_failure = false
-      result = result.gsub(/\b(before|on|in|after):(\((.+?)\)\B|(\S+)\b)/) do
+      result = result.gsub(/\b(before|on|in|during|after):(\((.+?)\)\B|(\S+)\b)/) do
         break if chronic_failure
         field, datestr = $1, ($3 || $4)
         realdate = Chronic.parse(datestr, :guess => false, :context => :none)
@@ -432,7 +450,11 @@ protected
     end
     
     Redwood::log "translated #{str.inspect} to #{result}" unless result == str
-    @qparser.parse result if result
+    if result
+      [@qparser.parse(result), extraopts]
+    else
+      [nil,nil]
+    end
   end
 
   def build_query opts
diff --git a/lib/sup/modes/search-results-mode.rb b/lib/sup/modes/search-results-mode.rb
@@ -1,9 +1,11 @@
 module Redwood
 
 class SearchResultsMode < ThreadIndexMode
-  def initialize qobj
+  def initialize qobj, qopts = nil
     @qobj = qobj
-    super [], { :qobj => @qobj }
+    @qopts = qopts
+
+    super [], { :qobj => @qobj }.merge(@qopts)
   end
 
   register_keymap do |k|
@@ -13,7 +15,7 @@ class SearchResultsMode < ThreadIndexMode
   def refine_search
     query = BufferManager.ask :search, "query: ", (@qobj.to_s + " ")
     return unless query && query !~ /^\s*$/
-    SearchResultsMode.spawn_from_query query
+    SearchResultsMode.spawn_from_query query, @qopts
   end
 
   ## a proper is_relevant? method requires some way of asking ferret
@@ -24,9 +26,10 @@ class SearchResultsMode < ThreadIndexMode
 
   def self.spawn_from_query text
     begin
-      qobj = Index.parse_user_query_string(text) or return
+      qobj, extraopts = Index.parse_user_query_string(text)
+      return unless qobj
       short_text = text.length < 20 ? text : text[0 ... 20] + "..."
-      mode = SearchResultsMode.new qobj
+      mode = SearchResultsMode.new qobj, extraopts
       BufferManager.spawn "search: \"#{short_text}\"", mode
       mode.load_threads :num => mode.buffer.content_height
     rescue Ferret::QueryParser::QueryParseException => e